2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
365 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367 return Roo.get(document.body);
371 * Fetch the element to display the tooltip on.
372 * @return {Roo.Element} defaults to this.el
374 tooltipEl : function()
379 addxtype : function(tree,cntr)
383 cn = Roo.factory(tree);
384 //Roo.log(['addxtype', cn]);
386 cn.parentType = this.xtype; //??
387 cn.parentId = this.id;
389 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390 if (typeof(cn.container_method) == 'string') {
391 cntr = cn.container_method;
395 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
397 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
399 var build_from_html = Roo.XComponent.build_from_html;
401 var is_body = (tree.xtype == 'Body') ;
403 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405 var self_cntr_el = Roo.get(this[cntr](false));
407 // do not try and build conditional elements
408 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414 return this.addxtypeChild(tree,cntr, is_body);
417 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
420 return this.addxtypeChild(Roo.apply({}, tree),cntr);
423 Roo.log('skipping render');
429 if (!build_from_html) {
433 // this i think handles overlaying multiple children of the same type
434 // with the sam eelement.. - which might be buggy..
436 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
442 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453 addxtypeChild : function (tree, cntr, is_body)
455 Roo.debug && Roo.log('addxtypeChild:' + cntr);
457 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
460 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461 (typeof(tree['flexy:foreach']) != 'undefined');
465 skip_children = false;
466 // render the element if it's not BODY.
469 // if parent was disabled, then do not try and create the children..
470 if(!this[cntr](true)){
475 cn = Roo.factory(tree);
477 cn.parentType = this.xtype; //??
478 cn.parentId = this.id;
480 var build_from_html = Roo.XComponent.build_from_html;
483 // does the container contain child eleemnts with 'xtype' attributes.
484 // that match this xtype..
485 // note - when we render we create these as well..
486 // so we should check to see if body has xtype set.
487 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489 var self_cntr_el = Roo.get(this[cntr](false));
490 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492 //Roo.log(Roo.XComponent.build_from_html);
493 //Roo.log("got echild:");
496 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497 // and are not displayed -this causes this to use up the wrong element when matching.
498 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
501 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
508 //echild.dom.removeAttribute('xtype');
510 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511 Roo.debug && Roo.log(self_cntr_el);
512 Roo.debug && Roo.log(echild);
513 Roo.debug && Roo.log(cn);
519 // if object has flexy:if - then it may or may not be rendered.
520 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
521 // skip a flexy if element.
522 Roo.debug && Roo.log('skipping render');
523 Roo.debug && Roo.log(tree);
525 Roo.debug && Roo.log('skipping all children');
526 skip_children = true;
531 // actually if flexy:foreach is found, we really want to create
532 // multiple copies here...
534 //Roo.log(this[cntr]());
535 // some elements do not have render methods.. like the layouts...
537 if(this[cntr](true) === false){
542 cn.render && cn.render(this[cntr](true));
545 // then add the element..
552 if (typeof (tree.menu) != 'undefined') {
553 tree.menu.parentType = cn.xtype;
554 tree.menu.triggerEl = cn.el;
555 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559 if (!tree.items || !tree.items.length) {
561 //Roo.log(["no children", this]);
566 var items = tree.items;
569 //Roo.log(items.length);
571 if (!skip_children) {
572 for(var i =0;i < items.length;i++) {
573 // Roo.log(['add child', items[i]]);
574 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
580 //Roo.log("fire childrenrendered");
582 cn.fireEvent('childrenrendered', this);
588 * Set the element that will be used to show or hide
590 setVisibilityEl : function(el)
592 this.visibilityEl = el;
596 * Get the element that will be used to show or hide
598 getVisibilityEl : function()
600 if (typeof(this.visibilityEl) == 'object') {
601 return this.visibilityEl;
604 if (typeof(this.visibilityEl) == 'string') {
605 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612 * Show a component - removes 'hidden' class
616 if(!this.getVisibilityEl()){
620 this.getVisibilityEl().removeClass(['hidden','d-none']);
622 this.fireEvent('show', this);
627 * Hide a component - adds 'hidden' class
631 if(!this.getVisibilityEl()){
635 this.getVisibilityEl().addClass(['hidden','d-none']);
637 this.fireEvent('hide', this);
650 * @class Roo.bootstrap.Element
651 * @extends Roo.bootstrap.Component
652 * Bootstrap Element class
653 * @cfg {String} html contents of the element
654 * @cfg {String} tag tag of the element
655 * @cfg {String} cls class of the element
656 * @cfg {Boolean} preventDefault (true|false) default false
657 * @cfg {Boolean} clickable (true|false) default false
658 * @cfg {String} role default blank - set to button to force cursor pointer
662 * Create a new Element
663 * @param {Object} config The config object
666 Roo.bootstrap.Element = function(config){
667 Roo.bootstrap.Element.superclass.constructor.call(this, config);
673 * When a element is chick
674 * @param {Roo.bootstrap.Element} this
675 * @param {Roo.EventObject} e
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
688 preventDefault: false,
693 getAutoCreate : function(){
697 // cls: this.cls, double assign in parent class Component.js :: onRender
700 if (this.role !== false) {
701 cfg.role = this.role;
707 initEvents: function()
709 Roo.bootstrap.Element.superclass.initEvents.call(this);
712 this.el.on('click', this.onClick, this);
718 onClick : function(e)
720 if(this.preventDefault){
724 this.fireEvent('click', this, e); // why was this double click before?
732 getValue : function()
734 return this.el.dom.innerHTML;
737 setValue : function(value)
739 this.el.dom.innerHTML = value;
754 * @class Roo.bootstrap.DropTarget
755 * @extends Roo.bootstrap.Element
756 * Bootstrap DropTarget class
758 * @cfg {string} name dropable name
761 * Create a new Dropable Area
762 * @param {Object} config The config object
765 Roo.bootstrap.DropTarget = function(config){
766 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772 * When a element is chick
773 * @param {Roo.bootstrap.Element} this
774 * @param {Roo.EventObject} e
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
783 getAutoCreate : function(){
788 initEvents: function()
790 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
794 drop : this.dragDrop.createDelegate(this),
795 enter : this.dragEnter.createDelegate(this),
796 out : this.dragOut.createDelegate(this),
797 over : this.dragOver.createDelegate(this)
801 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
804 dragDrop : function(source,e,data)
806 // user has to decide how to impliment this.
809 //this.fireEvent('drop', this, source, e ,data);
813 dragEnter : function(n, dd, e, data)
815 // probably want to resize the element to match the dropped element..
817 this.originalSize = this.el.getSize();
818 this.el.setSize( n.el.getSize());
819 this.dropZone.DDM.refreshCache(this.name);
820 Roo.log([n, dd, e, data]);
823 dragOut : function(value)
825 // resize back to normal
827 this.el.setSize(this.originalSize);
828 this.dropZone.resetConstraints();
831 dragOver : function()
848 * @class Roo.bootstrap.Body
849 * @extends Roo.bootstrap.Component
850 * Bootstrap Body class
854 * @param {Object} config The config object
857 Roo.bootstrap.Body = function(config){
859 config = config || {};
861 Roo.bootstrap.Body.superclass.constructor.call(this, config);
862 this.el = Roo.get(config.el ? config.el : document.body );
863 if (this.cls && this.cls.length) {
864 Roo.get(document.body).addClass(this.cls);
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
870 is_body : true,// just to make sure it's constructed?
875 onRender : function(ct, position)
877 /* Roo.log("Roo.bootstrap.Body - onRender");
878 if (this.cls && this.cls.length) {
879 Roo.get(document.body).addClass(this.cls);
898 * @class Roo.bootstrap.ButtonGroup
899 * @extends Roo.bootstrap.Component
900 * Bootstrap ButtonGroup class
901 * @cfg {String} size lg | sm | xs (default empty normal)
902 * @cfg {String} align vertical | justified (default none)
903 * @cfg {String} direction up | down (default down)
904 * @cfg {Boolean} toolbar false | true
905 * @cfg {Boolean} btn true | false
910 * @param {Object} config The config object
913 Roo.bootstrap.ButtonGroup = function(config){
914 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
925 getAutoCreate : function(){
931 cfg.html = this.html || cfg.html;
942 if (['vertical','justified'].indexOf(this.align)!==-1) {
943 cfg.cls = 'btn-group-' + this.align;
945 if (this.align == 'justified') {
946 console.log(this.items);
950 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951 cfg.cls += ' btn-group-' + this.size;
954 if (this.direction == 'up') {
955 cfg.cls += ' dropup' ;
961 * Add a button to the group (similar to NavItem API.)
963 addItem : function(cfg)
965 var cn = new Roo.bootstrap.Button(cfg);
967 cn.parentId = this.id;
968 cn.onRender(this.el, null);
982 * @class Roo.bootstrap.Button
983 * @extends Roo.bootstrap.Component
984 * Bootstrap Button class
985 * @cfg {String} html The button content
986 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989 * @cfg {String} size (lg|sm|xs)
990 * @cfg {String} tag (a|input|submit)
991 * @cfg {String} href empty or href
992 * @cfg {Boolean} disabled default false;
993 * @cfg {Boolean} isClose default false;
994 * @cfg {String} glyphicon depricated - use fa
995 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996 * @cfg {String} badge text for badge
997 * @cfg {String} theme (default|glow)
998 * @cfg {Boolean} inverse dark themed version
999 * @cfg {Boolean} toggle is it a slidy toggle button
1000 * @cfg {Boolean} pressed default null - if the button ahs active state
1001 * @cfg {String} ontext text for on slidy toggle state
1002 * @cfg {String} offtext text for off slidy toggle state
1003 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1004 * @cfg {Boolean} removeClass remove the standard class..
1005 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1006 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1009 * Create a new button
1010 * @param {Object} config The config object
1014 Roo.bootstrap.Button = function(config){
1015 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1021 * When a button is pressed
1022 * @param {Roo.bootstrap.Button} btn
1023 * @param {Roo.EventObject} e
1028 * When a button is double clicked
1029 * @param {Roo.bootstrap.Button} btn
1030 * @param {Roo.EventObject} e
1035 * After the button has been toggles
1036 * @param {Roo.bootstrap.Button} btn
1037 * @param {Roo.EventObject} e
1038 * @param {boolean} pressed (also available as button.pressed)
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1065 preventDefault: true,
1074 getAutoCreate : function(){
1082 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084 this.tag = 'button';
1088 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090 if (this.toggle == true) {
1093 cls: 'slider-frame roo-button',
1097 'data-on-text':'ON',
1098 'data-off-text':'OFF',
1099 cls: 'slider-button',
1104 // why are we validating the weights?
1105 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106 cfg.cls += ' ' + this.weight;
1113 cfg.cls += ' close';
1115 cfg["aria-hidden"] = true;
1117 cfg.html = "×";
1123 if (this.theme==='default') {
1124 cfg.cls = 'btn roo-button';
1126 //if (this.parentType != 'Navbar') {
1127 this.weight = this.weight.length ? this.weight : 'default';
1129 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133 cfg.cls += ' btn-' + outline + weight;
1134 if (this.weight == 'default') {
1136 cfg.cls += ' btn-' + this.weight;
1139 } else if (this.theme==='glow') {
1142 cfg.cls = 'btn-glow roo-button';
1144 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146 cfg.cls += ' ' + this.weight;
1152 this.cls += ' inverse';
1156 if (this.active || this.pressed === true) {
1157 cfg.cls += ' active';
1160 if (this.disabled) {
1161 cfg.disabled = 'disabled';
1165 Roo.log('changing to ul' );
1167 this.glyphicon = 'caret';
1168 if (Roo.bootstrap.version == 4) {
1169 this.fa = 'caret-down';
1174 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176 //gsRoo.log(this.parentType);
1177 if (this.parentType === 'Navbar' && !this.parent().bar) {
1178 Roo.log('changing to li?');
1187 href : this.href || '#'
1190 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1191 cfg.cls += ' dropdown';
1198 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1200 if (this.glyphicon) {
1201 cfg.html = ' ' + cfg.html;
1206 cls: 'glyphicon glyphicon-' + this.glyphicon
1211 cfg.html = ' ' + cfg.html;
1216 cls: 'fa fas fa-' + this.fa
1226 // cfg.cls='btn roo-button';
1230 var value = cfg.html;
1235 cls: 'glyphicon glyphicon-' + this.glyphicon,
1242 cls: 'fa fas fa-' + this.fa,
1247 var bw = this.badge_weight.length ? this.badge_weight :
1248 (this.weight.length ? this.weight : 'secondary');
1249 bw = bw == 'default' ? 'secondary' : bw;
1255 cls: 'badge badge-' + bw,
1264 cfg.cls += ' dropdown';
1265 cfg.html = typeof(cfg.html) != 'undefined' ?
1266 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1269 if (cfg.tag !== 'a' && this.href !== '') {
1270 throw "Tag must be a to set href.";
1271 } else if (this.href.length > 0) {
1272 cfg.href = this.href;
1275 if(this.removeClass){
1280 cfg.target = this.target;
1285 initEvents: function() {
1286 // Roo.log('init events?');
1287 // Roo.log(this.el.dom);
1290 if (typeof (this.menu) != 'undefined') {
1291 this.menu.parentType = this.xtype;
1292 this.menu.triggerEl = this.el;
1293 this.addxtype(Roo.apply({}, this.menu));
1297 if (this.el.hasClass('roo-button')) {
1298 this.el.on('click', this.onClick, this);
1299 this.el.on('dblclick', this.onDblClick, this);
1301 this.el.select('.roo-button').on('click', this.onClick, this);
1302 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1306 if(this.removeClass){
1307 this.el.on('click', this.onClick, this);
1310 if (this.group === true) {
1311 if (this.pressed === false || this.pressed === true) {
1314 this.pressed = false;
1315 this.setActive(this.pressed);
1320 this.el.enableDisplayMode();
1323 onClick : function(e)
1325 if (this.disabled) {
1329 Roo.log('button on click ');
1330 if(this.preventDefault){
1339 this.setActive(true);
1340 var pi = this.parent().items;
1341 for (var i = 0;i < pi.length;i++) {
1342 if (this == pi[i]) {
1345 if (pi[i].el.hasClass('roo-button')) {
1346 pi[i].setActive(false);
1349 this.fireEvent('click', this, e);
1353 if (this.pressed === true || this.pressed === false) {
1354 this.toggleActive(e);
1358 this.fireEvent('click', this, e);
1360 onDblClick: function(e)
1362 if (this.disabled) {
1365 if(this.preventDefault){
1368 this.fireEvent('dblclick', this, e);
1371 * Enables this button
1375 this.disabled = false;
1376 this.el.removeClass('disabled');
1377 this.el.dom.removeAttribute("disabled");
1381 * Disable this button
1383 disable : function()
1385 this.disabled = true;
1386 this.el.addClass('disabled');
1387 this.el.attr("disabled", "disabled")
1390 * sets the active state on/off,
1391 * @param {Boolean} state (optional) Force a particular state
1393 setActive : function(v) {
1395 this.el[v ? 'addClass' : 'removeClass']('active');
1399 * toggles the current active state
1401 toggleActive : function(e)
1403 this.setActive(!this.pressed); // this modifies pressed...
1404 this.fireEvent('toggle', this, e, this.pressed);
1407 * get the current active state
1408 * @return {boolean} true if it's active
1410 isActive : function()
1412 return this.el.hasClass('active');
1415 * set the text of the first selected button
1417 setText : function(str)
1419 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1422 * get the text of the first selected button
1424 getText : function()
1426 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1429 setWeight : function(str)
1431 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434 var outline = this.outline ? 'outline-' : '';
1435 if (str == 'default') {
1436 this.el.addClass('btn-default btn-outline-secondary');
1439 this.el.addClass('btn-' + outline + str);
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446 Roo.bootstrap.Button.weights = [
1466 * @class Roo.bootstrap.Column
1467 * @extends Roo.bootstrap.Component
1468 * Bootstrap Column class
1469 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1479 * @cfg {Boolean} hidden (true|false) hide the element
1480 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481 * @cfg {String} fa (ban|check|...) font awesome icon
1482 * @cfg {Number} fasize (1|2|....) font awsome size
1484 * @cfg {String} icon (info-sign|check|...) glyphicon name
1486 * @cfg {String} html content of column.
1489 * Create a new Column
1490 * @param {Object} config The config object
1493 Roo.bootstrap.Column = function(config){
1494 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1515 getAutoCreate : function(){
1516 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1524 var sizes = ['xs','sm','md','lg'];
1525 sizes.map(function(size ,ix){
1526 //Roo.log( size + ':' + settings[size]);
1528 if (settings[size+'off'] !== false) {
1529 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1532 if (settings[size] === false) {
1536 if (!settings[size]) { // 0 = hidden
1537 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539 for (var i = ix; i > -1; i--) {
1540 cfg.cls += ' d-' + sizes[i] + '-none';
1546 cfg.cls += ' col-' + size + '-' + settings[size] + (
1547 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1553 cfg.cls += ' hidden';
1556 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557 cfg.cls +=' alert alert-' + this.alert;
1561 if (this.html.length) {
1562 cfg.html = this.html;
1566 if (this.fasize > 1) {
1567 fasize = ' fa-' + this.fasize + 'x';
1569 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1574 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1593 * @class Roo.bootstrap.Container
1594 * @extends Roo.bootstrap.Component
1595 * Bootstrap Container class
1596 * @cfg {Boolean} jumbotron is it a jumbotron element
1597 * @cfg {String} html content of element
1598 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1600 * @cfg {String} header content of header (for panel)
1601 * @cfg {String} footer content of footer (for panel)
1602 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603 * @cfg {String} tag (header|aside|section) type of HTML tag.
1604 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605 * @cfg {String} fa font awesome icon
1606 * @cfg {String} icon (info-sign|check|...) glyphicon name
1607 * @cfg {Boolean} hidden (true|false) hide the element
1608 * @cfg {Boolean} expandable (true|false) default false
1609 * @cfg {Boolean} expanded (true|false) default true
1610 * @cfg {String} rheader contet on the right of header
1611 * @cfg {Boolean} clickable (true|false) default false
1615 * Create a new Container
1616 * @param {Object} config The config object
1619 Roo.bootstrap.Container = function(config){
1620 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1626 * After the panel has been expand
1628 * @param {Roo.bootstrap.Container} this
1633 * After the panel has been collapsed
1635 * @param {Roo.bootstrap.Container} this
1640 * When a element is chick
1641 * @param {Roo.bootstrap.Container} this
1642 * @param {Roo.EventObject} e
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1666 getChildContainer : function() {
1672 if (this.panel.length) {
1673 return this.el.select('.panel-body',true).first();
1680 getAutoCreate : function(){
1683 tag : this.tag || 'div',
1687 if (this.jumbotron) {
1688 cfg.cls = 'jumbotron';
1693 // - this is applied by the parent..
1695 // cfg.cls = this.cls + '';
1698 if (this.sticky.length) {
1700 var bd = Roo.get(document.body);
1701 if (!bd.hasClass('bootstrap-sticky')) {
1702 bd.addClass('bootstrap-sticky');
1703 Roo.select('html',true).setStyle('height', '100%');
1706 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1710 if (this.well.length) {
1711 switch (this.well) {
1714 cfg.cls +=' well well-' +this.well;
1723 cfg.cls += ' hidden';
1727 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728 cfg.cls +=' alert alert-' + this.alert;
1733 if (this.panel.length) {
1734 cfg.cls += ' panel panel-' + this.panel;
1736 if (this.header.length) {
1740 if(this.expandable){
1742 cfg.cls = cfg.cls + ' expandable';
1746 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1754 cls : 'panel-title',
1755 html : (this.expandable ? ' ' : '') + this.header
1759 cls: 'panel-header-right',
1765 cls : 'panel-heading',
1766 style : this.expandable ? 'cursor: pointer' : '',
1774 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1779 if (this.footer.length) {
1781 cls : 'panel-footer',
1790 body.html = this.html || cfg.html;
1791 // prefix with the icons..
1793 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1796 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1801 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802 cfg.cls = 'container';
1808 initEvents: function()
1810 if(this.expandable){
1811 var headerEl = this.headerEl();
1814 headerEl.on('click', this.onToggleClick, this);
1819 this.el.on('click', this.onClick, this);
1824 onToggleClick : function()
1826 var headerEl = this.headerEl();
1842 if(this.fireEvent('expand', this)) {
1844 this.expanded = true;
1846 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848 this.el.select('.panel-body',true).first().removeClass('hide');
1850 var toggleEl = this.toggleEl();
1856 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1861 collapse : function()
1863 if(this.fireEvent('collapse', this)) {
1865 this.expanded = false;
1867 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868 this.el.select('.panel-body',true).first().addClass('hide');
1870 var toggleEl = this.toggleEl();
1876 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1880 toggleEl : function()
1882 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1886 return this.el.select('.panel-heading .fa',true).first();
1889 headerEl : function()
1891 if(!this.el || !this.panel.length || !this.header.length){
1895 return this.el.select('.panel-heading',true).first()
1900 if(!this.el || !this.panel.length){
1904 return this.el.select('.panel-body',true).first()
1907 titleEl : function()
1909 if(!this.el || !this.panel.length || !this.header.length){
1913 return this.el.select('.panel-title',true).first();
1916 setTitle : function(v)
1918 var titleEl = this.titleEl();
1924 titleEl.dom.innerHTML = v;
1927 getTitle : function()
1930 var titleEl = this.titleEl();
1936 return titleEl.dom.innerHTML;
1939 setRightTitle : function(v)
1941 var t = this.el.select('.panel-header-right',true).first();
1947 t.dom.innerHTML = v;
1950 onClick : function(e)
1954 this.fireEvent('click', this, e);
1961 * This is BS4's Card element.. - similar to our containers probably..
1965 * @class Roo.bootstrap.Card
1966 * @extends Roo.bootstrap.Component
1967 * Bootstrap Card class
1970 * possible... may not be implemented..
1971 * @cfg {String} header_image src url of image.
1972 * @cfg {String|Object} header
1973 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1976 * @cfg {String} title
1977 * @cfg {String} subtitle
1978 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979 * @cfg {String} footer
1981 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983 * @cfg {String} margin (0|1|2|3|4|5|auto)
1984 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991 * @cfg {String} padding (0|1|2|3|4|5)
1992 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994 * @cfg {String} padding_left (0|1|2|3|4|5)
1995 * @cfg {String} padding_right (0|1|2|3|4|5)
1996 * @cfg {String} padding_x (0|1|2|3|4|5)
1997 * @cfg {String} padding_y (0|1|2|3|4|5)
1999 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @config {Boolean} dragable if this card can be dragged.
2006 * @config {String} drag_group group for drag
2007 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2008 * @config {String} drop_group group for drag
2010 * @config {Boolean} collapsable can the body be collapsed.
2011 * @config {Boolean} collapsed is the body collapsed when rendered...
2012 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013 * @config {Boolean} rotated is the body rotated when rendered...
2016 * Create a new Container
2017 * @param {Object} config The config object
2020 Roo.bootstrap.Card = function(config){
2021 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2027 * When a element a card is dropped
2028 * @param {Roo.bootstrap.Card} this
2031 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032 * @param {String} position 'above' or 'below'
2033 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2039 * When a element a card is rotate
2040 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.Element} n the node being dropped?
2042 * @param {Boolean} rotate status
2047 * When a card element is dragged over ready to drop (return false to block dropable)
2048 * @param {Roo.bootstrap.Card} this
2049 * @param {Object} data from dragdrop
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2062 margin: '', /// may be better in component?
2092 collapsable : false,
2101 childContainer : false,
2102 dropEl : false, /// the dom placeholde element that indicates drop location.
2103 containerEl: false, // body container
2104 bodyEl: false, // card-body
2105 headerContainerEl : false, //
2107 header_imageEl : false,
2110 layoutCls : function()
2114 Roo.log(this.margin_bottom.length);
2115 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2121 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2126 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2132 // more generic support?
2140 // Roo.log("Call onRender: " + this.xtype);
2141 /* We are looking at something like this.
2143 <img src="..." class="card-img-top" alt="...">
2144 <div class="card-body">
2145 <h5 class="card-title">Card title</h5>
2146 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148 >> this bit is really the body...
2149 <div> << we will ad dthis in hopefully it will not break shit.
2151 ** card text does not actually have any styling...
2153 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2156 <a href="#" class="card-link">Card link</a>
2159 <div class="card-footer">
2160 <small class="text-muted">Last updated 3 mins ago</small>
2164 getAutoCreate : function(){
2172 if (this.weight.length && this.weight != 'light') {
2173 cfg.cls += ' text-white';
2175 cfg.cls += ' text-dark'; // need as it's nested..
2177 if (this.weight.length) {
2178 cfg.cls += ' bg-' + this.weight;
2181 cfg.cls += ' ' + this.layoutCls();
2184 var hdr_ctr = false;
2185 if (this.header.length) {
2187 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2196 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202 if (this.collapsable) {
2205 cls : 'd-block user-select-none',
2209 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2214 hdr.cn.push(hdr_ctr);
2219 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2224 if (this.header_image.length) {
2227 cls : 'card-img-top',
2228 src: this.header_image // escape?
2233 cls : 'card-img-top d-none'
2239 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2243 if (this.collapsable || this.rotateable) {
2246 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2253 if (this.title.length) {
2257 src: this.title // escape?
2261 if (this.subtitle.length) {
2265 src: this.subtitle // escape?
2271 cls : 'roo-card-body-ctr'
2274 if (this.html.length) {
2280 // fixme ? handle objects?
2282 if (this.footer.length) {
2285 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2290 cfg.cn.push({cls : 'card-footer d-none'});
2299 getCardHeader : function()
2301 var ret = this.el.select('.card-header',true).first();
2302 if (ret.hasClass('d-none')) {
2303 ret.removeClass('d-none');
2308 getCardFooter : function()
2310 var ret = this.el.select('.card-footer',true).first();
2311 if (ret.hasClass('d-none')) {
2312 ret.removeClass('d-none');
2317 getCardImageTop : function()
2319 var ret = this.header_imageEl;
2320 if (ret.hasClass('d-none')) {
2321 ret.removeClass('d-none');
2327 getChildContainer : function()
2333 return this.el.select('.roo-card-body-ctr',true).first();
2336 initEvents: function()
2338 this.bodyEl = this.el.select('.card-body',true).first();
2339 this.containerEl = this.getChildContainer();
2341 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342 containerScroll: true,
2343 ddGroup: this.drag_group || 'default_card_drag_group'
2345 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347 if (this.dropable) {
2348 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349 containerScroll: true,
2350 ddGroup: this.drop_group || 'default_card_drag_group'
2352 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2359 if (this.collapsable) {
2360 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362 if (this.rotateable) {
2363 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367 this.footerEl = this.el.select('.card-footer',true).first();
2368 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370 this.headerEl = this.el.select('.card-header',true).first();
2373 this.el.addClass('roo-card-rotated');
2374 this.fireEvent('rotate', this, true);
2376 this.header_imageEl = this.el.select('.card-img-top',true).first();
2377 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2380 getDragData : function(e)
2382 var target = this.getEl();
2384 //this.handleSelection(e);
2389 nodes: this.getEl(),
2394 dragData.ddel = target.dom ; // the div element
2395 Roo.log(target.getWidth( ));
2396 dragData.ddel.style.width = target.getWidth() + 'px';
2403 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2404 * whole Element becomes the target, and this causes the drop gesture to append.
2406 * Returns an object:
2409 position : 'below' or 'above'
2410 card : relateive to card OBJECT (or true for no cards listed)
2411 items_n : relative to nth item in list
2412 card_n : relative to nth card in list
2417 getTargetFromEvent : function(e, dragged_card_el)
2419 var target = e.getTarget();
2420 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421 target = target.parentNode;
2432 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433 // see if target is one of the 'cards'...
2436 //Roo.log(this.items.length);
2439 var last_card_n = 0;
2441 for (var i = 0;i< this.items.length;i++) {
2443 if (!this.items[i].el.hasClass('card')) {
2446 pos = this.getDropPoint(e, this.items[i].el.dom);
2448 cards_len = ret.cards.length;
2449 //Roo.log(this.items[i].el.dom.id);
2450 ret.cards.push(this.items[i]);
2452 if (ret.card_n < 0 && pos == 'above') {
2453 ret.position = cards_len > 0 ? 'below' : pos;
2454 ret.items_n = i > 0 ? i - 1 : 0;
2455 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2456 ret.card = ret.cards[ret.card_n];
2459 if (!ret.cards.length) {
2461 ret.position = 'below';
2465 // could not find a card.. stick it at the end..
2466 if (ret.card_n < 0) {
2467 ret.card_n = last_card_n;
2468 ret.card = ret.cards[last_card_n];
2469 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470 ret.position = 'below';
2473 if (this.items[ret.items_n].el == dragged_card_el) {
2477 if (ret.position == 'below') {
2478 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480 if (card_after && card_after.el == dragged_card_el) {
2487 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489 if (card_before && card_before.el == dragged_card_el) {
2496 onNodeEnter : function(n, dd, e, data){
2499 onNodeOver : function(n, dd, e, data)
2502 var target_info = this.getTargetFromEvent(e,data.source.el);
2503 if (target_info === false) {
2504 this.dropPlaceHolder('hide');
2507 Roo.log(['getTargetFromEvent', target_info ]);
2510 if (this.fireEvent('cardover', this, [ data ]) === false) {
2514 this.dropPlaceHolder('show', target_info,data);
2518 onNodeOut : function(n, dd, e, data){
2519 this.dropPlaceHolder('hide');
2522 onNodeDrop : function(n, dd, e, data)
2525 // call drop - return false if
2527 // this could actually fail - if the Network drops..
2528 // we will ignore this at present..- client should probably reload
2529 // the whole set of cards if stuff like that fails.
2532 var info = this.getTargetFromEvent(e,data.source.el);
2533 if (info === false) {
2536 this.dropPlaceHolder('hide');
2540 this.acceptCard(data.source, info.position, info.card, info.items_n);
2544 firstChildCard : function()
2546 for (var i = 0;i< this.items.length;i++) {
2548 if (!this.items[i].el.hasClass('card')) {
2551 return this.items[i];
2553 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2558 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2560 acceptCard : function(move_card, position, next_to_card )
2562 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2566 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568 move_card.parent().removeCard(move_card);
2571 var dom = move_card.el.dom;
2572 dom.style.width = ''; // clear with - which is set by drag.
2574 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575 var cardel = next_to_card.el.dom;
2577 if (position == 'above' ) {
2578 cardel.parentNode.insertBefore(dom, cardel);
2579 } else if (cardel.nextSibling) {
2580 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582 cardel.parentNode.append(dom);
2585 // card container???
2586 this.containerEl.dom.append(dom);
2589 //FIXME HANDLE card = true
2591 // add this to the correct place in items.
2593 // remove Card from items.
2596 if (this.items.length) {
2598 //Roo.log([info.items_n, info.position, this.items.length]);
2599 for (var i =0; i < this.items.length; i++) {
2600 if (i == to_items_n && position == 'above') {
2601 nitems.push(move_card);
2603 nitems.push(this.items[i]);
2604 if (i == to_items_n && position == 'below') {
2605 nitems.push(move_card);
2608 this.items = nitems;
2609 Roo.log(this.items);
2611 this.items.push(move_card);
2614 move_card.parentId = this.id;
2620 removeCard : function(c)
2622 this.items = this.items.filter(function(e) { return e != c });
2625 dom.parentNode.removeChild(dom);
2626 dom.style.width = ''; // clear with - which is set by drag.
2631 /** Decide whether to drop above or below a View node. */
2632 getDropPoint : function(e, n, dd)
2637 if (n == this.containerEl.dom) {
2640 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641 var c = t + (b - t) / 2;
2642 var y = Roo.lib.Event.getPageY(e);
2649 onToggleCollapse : function(e)
2651 if (this.collapsed) {
2652 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653 this.collapsableEl.addClass('show');
2654 this.collapsed = false;
2657 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658 this.collapsableEl.removeClass('show');
2659 this.collapsed = true;
2664 onToggleRotate : function(e)
2666 this.collapsableEl.removeClass('show');
2667 this.footerEl.removeClass('d-none');
2668 this.el.removeClass('roo-card-rotated');
2669 this.el.removeClass('d-none');
2672 this.collapsableEl.addClass('show');
2673 this.rotated = false;
2674 this.fireEvent('rotate', this, this.rotated);
2677 this.el.addClass('roo-card-rotated');
2678 this.footerEl.addClass('d-none');
2679 this.el.select('.roo-collapsable').removeClass('show');
2681 this.rotated = true;
2682 this.fireEvent('rotate', this, this.rotated);
2686 dropPlaceHolder: function (action, info, data)
2688 if (this.dropEl === false) {
2689 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2693 this.dropEl.removeClass(['d-none', 'd-block']);
2694 if (action == 'hide') {
2696 this.dropEl.addClass('d-none');
2699 // FIXME - info.card == true!!!
2700 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702 if (info.card !== true) {
2703 var cardel = info.card.el.dom;
2705 if (info.position == 'above') {
2706 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707 } else if (cardel.nextSibling) {
2708 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710 cardel.parentNode.append(this.dropEl.dom);
2713 // card container???
2714 this.containerEl.dom.append(this.dropEl.dom);
2717 this.dropEl.addClass('d-block roo-card-dropzone');
2719 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2726 setHeaderText: function(html)
2729 if (this.headerContainerEl) {
2730 this.headerContainerEl.dom.innerHTML = html;
2733 onHeaderImageLoad : function(ev, he)
2735 if (!this.header_image_fit_square) {
2739 var hw = he.naturalHeight / he.naturalWidth;
2742 //var w = he.dom.naturalWidth;
2745 he.style.position = 'relative';
2747 var nw = (ww * (1/hw));
2748 Roo.get(he).setSize( ww * (1/hw), ww);
2749 he.style.left = ((ww - nw)/ 2) + 'px';
2750 he.style.position = 'relative';
2761 * Card header - holder for the card header elements.
2766 * @class Roo.bootstrap.CardHeader
2767 * @extends Roo.bootstrap.Element
2768 * Bootstrap CardHeader class
2770 * Create a new Card Header - that you can embed children into
2771 * @param {Object} config The config object
2774 Roo.bootstrap.CardHeader = function(config){
2775 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2781 container_method : 'getCardHeader'
2794 * Card footer - holder for the card footer elements.
2799 * @class Roo.bootstrap.CardFooter
2800 * @extends Roo.bootstrap.Element
2801 * Bootstrap CardFooter class
2803 * Create a new Card Footer - that you can embed children into
2804 * @param {Object} config The config object
2807 Roo.bootstrap.CardFooter = function(config){
2808 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2814 container_method : 'getCardFooter'
2827 * Card header - holder for the card header elements.
2832 * @class Roo.bootstrap.CardImageTop
2833 * @extends Roo.bootstrap.Element
2834 * Bootstrap CardImageTop class
2836 * Create a new Card Image Top container
2837 * @param {Object} config The config object
2840 Roo.bootstrap.CardImageTop = function(config){
2841 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2847 container_method : 'getCardImageTop'
2862 * @class Roo.bootstrap.ButtonUploader
2863 * @extends Roo.bootstrap.Button
2864 * Bootstrap Button Uploader class - it's a button which when you add files to it
2867 * @cfg {Number} errorTimeout default 3000
2868 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2869 * @cfg {Array} html The button text.
2870 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2873 * Create a new CardUploader
2874 * @param {Object} config The config object
2877 Roo.bootstrap.ButtonUploader = function(config){
2881 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2887 * @event beforeselect
2888 * When button is pressed, before show upload files dialog is shown
2889 * @param {Roo.bootstrap.UploaderButton} this
2892 'beforeselect' : true,
2894 * @event fired when files have been selected,
2895 * When a the download link is clicked
2896 * @param {Roo.bootstrap.UploaderButton} this
2897 * @param {Array} Array of files that have been uploaded
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2907 errorTimeout : 3000,
2911 fileCollection : false,
2916 getAutoCreate : function()
2921 cls : 'd-none roo-card-upload-selector'
2924 if (this.multiple) {
2925 im.multiple = 'multiple';
2931 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2941 initEvents : function()
2944 Roo.bootstrap.Button.prototype.initEvents.call(this);
2950 this.urlAPI = (window.createObjectURL && window) ||
2951 (window.URL && URL.revokeObjectURL && URL) ||
2952 (window.webkitURL && webkitURL);
2957 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959 this.selectorEl.on('change', this.onFileSelected, this);
2966 onClick : function(e)
2970 if ( this.fireEvent('beforeselect', this) === false) {
2974 this.selectorEl.dom.click();
2978 onFileSelected : function(e)
2982 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2985 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986 this.selectorEl.dom.value = '';// hopefully reset..
2988 this.fireEvent('uploaded', this, files );
2996 * addCard - add an Attachment to the uploader
2997 * @param data - the data about the image to upload
3001 title : "Title of file",
3002 is_uploaded : false,
3003 src : "http://.....",
3004 srcfile : { the File upload object },
3005 mimetype : file.type,
3008 .. any other data...
3033 * @class Roo.bootstrap.Img
3034 * @extends Roo.bootstrap.Component
3035 * Bootstrap Img class
3036 * @cfg {Boolean} imgResponsive false | true
3037 * @cfg {String} border rounded | circle | thumbnail
3038 * @cfg {String} src image source
3039 * @cfg {String} alt image alternative text
3040 * @cfg {String} href a tag href
3041 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042 * @cfg {String} xsUrl xs image source
3043 * @cfg {String} smUrl sm image source
3044 * @cfg {String} mdUrl md image source
3045 * @cfg {String} lgUrl lg image source
3048 * Create a new Input
3049 * @param {Object} config The config object
3052 Roo.bootstrap.Img = function(config){
3053 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3059 * The img click event for the img.
3060 * @param {Roo.EventObject} e
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3068 imgResponsive: true,
3078 getAutoCreate : function()
3080 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081 return this.createSingleImg();
3086 cls: 'roo-image-responsive-group',
3091 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3093 if(!_this[size + 'Url']){
3099 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100 html: _this.html || cfg.html,
3101 src: _this[size + 'Url']
3104 img.cls += ' roo-image-responsive-' + size;
3106 var s = ['xs', 'sm', 'md', 'lg'];
3108 s.splice(s.indexOf(size), 1);
3110 Roo.each(s, function(ss){
3111 img.cls += ' hidden-' + ss;
3114 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115 cfg.cls += ' img-' + _this.border;
3119 cfg.alt = _this.alt;
3132 a.target = _this.target;
3136 cfg.cn.push((_this.href) ? a : img);
3143 createSingleImg : function()
3147 cls: (this.imgResponsive) ? 'img-responsive' : '',
3149 src : 'about:blank' // just incase src get's set to undefined?!?
3152 cfg.html = this.html || cfg.html;
3154 cfg.src = this.src || cfg.src;
3156 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157 cfg.cls += ' img-' + this.border;
3174 a.target = this.target;
3179 return (this.href) ? a : cfg;
3182 initEvents: function()
3185 this.el.on('click', this.onClick, this);
3190 onClick : function(e)
3192 Roo.log('img onclick');
3193 this.fireEvent('click', this, e);
3196 * Sets the url of the image - used to update it
3197 * @param {String} url the url of the image
3200 setSrc : function(url)
3204 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205 this.el.dom.src = url;
3209 this.el.select('img', true).first().dom.src = url;
3225 * @class Roo.bootstrap.Link
3226 * @extends Roo.bootstrap.Component
3227 * Bootstrap Link Class
3228 * @cfg {String} alt image alternative text
3229 * @cfg {String} href a tag href
3230 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231 * @cfg {String} html the content of the link.
3232 * @cfg {String} anchor name for the anchor link
3233 * @cfg {String} fa - favicon
3235 * @cfg {Boolean} preventDefault (true | false) default false
3239 * Create a new Input
3240 * @param {Object} config The config object
3243 Roo.bootstrap.Link = function(config){
3244 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3250 * The img click event for the img.
3251 * @param {Roo.EventObject} e
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3261 preventDefault: false,
3267 getAutoCreate : function()
3269 var html = this.html || '';
3271 if (this.fa !== false) {
3272 html = '<i class="fa fa-' + this.fa + '"></i>';
3277 // anchor's do not require html/href...
3278 if (this.anchor === false) {
3280 cfg.href = this.href || '#';
3282 cfg.name = this.anchor;
3283 if (this.html !== false || this.fa !== false) {
3286 if (this.href !== false) {
3287 cfg.href = this.href;
3291 if(this.alt !== false){
3296 if(this.target !== false) {
3297 cfg.target = this.target;
3303 initEvents: function() {
3305 if(!this.href || this.preventDefault){
3306 this.el.on('click', this.onClick, this);
3310 onClick : function(e)
3312 if(this.preventDefault){
3315 //Roo.log('img onclick');
3316 this.fireEvent('click', this, e);
3329 * @class Roo.bootstrap.Header
3330 * @extends Roo.bootstrap.Component
3331 * Bootstrap Header class
3332 * @cfg {String} html content of header
3333 * @cfg {Number} level (1|2|3|4|5|6) default 1
3336 * Create a new Header
3337 * @param {Object} config The config object
3341 Roo.bootstrap.Header = function(config){
3342 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3353 getAutoCreate : function(){
3358 tag: 'h' + (1 *this.level),
3359 html: this.html || ''
3371 * Ext JS Library 1.1.1
3372 * Copyright(c) 2006-2007, Ext JS, LLC.
3374 * Originally Released Under LGPL - original licence link has changed is not relivant.
3377 * <script type="text/javascript">
3381 * @class Roo.bootstrap.MenuMgr
3382 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3385 Roo.bootstrap.MenuMgr = function(){
3386 var menus, active, groups = {}, attached = false, lastShow = new Date();
3388 // private - called when first menu is created
3391 active = new Roo.util.MixedCollection();
3392 Roo.get(document).addKeyListener(27, function(){
3393 if(active.length > 0){
3401 if(active && active.length > 0){
3402 var c = active.clone();
3412 if(active.length < 1){
3413 Roo.get(document).un("mouseup", onMouseDown);
3421 var last = active.last();
3422 lastShow = new Date();
3425 Roo.get(document).on("mouseup", onMouseDown);
3430 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431 m.parentMenu.activeChild = m;
3432 }else if(last && last.isVisible()){
3433 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3438 function onBeforeHide(m){
3440 m.activeChild.hide();
3442 if(m.autoHideTimer){
3443 clearTimeout(m.autoHideTimer);
3444 delete m.autoHideTimer;
3449 function onBeforeShow(m){
3450 var pm = m.parentMenu;
3451 if(!pm && !m.allowOtherMenus){
3453 }else if(pm && pm.activeChild && active != m){
3454 pm.activeChild.hide();
3458 // private this should really trigger on mouseup..
3459 function onMouseDown(e){
3460 Roo.log("on Mouse Up");
3462 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463 Roo.log("MenuManager hideAll");
3472 function onBeforeCheck(mi, state){
3474 var g = groups[mi.group];
3475 for(var i = 0, l = g.length; i < l; i++){
3477 g[i].setChecked(false);
3486 * Hides all menus that are currently visible
3488 hideAll : function(){
3493 register : function(menu){
3497 menus[menu.id] = menu;
3498 menu.on("beforehide", onBeforeHide);
3499 menu.on("hide", onHide);
3500 menu.on("beforeshow", onBeforeShow);
3501 menu.on("show", onShow);
3503 if(g && menu.events["checkchange"]){
3507 groups[g].push(menu);
3508 menu.on("checkchange", onCheck);
3513 * Returns a {@link Roo.menu.Menu} object
3514 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515 * be used to generate and return a new Menu instance.
3517 get : function(menu){
3518 if(typeof menu == "string"){ // menu id
3520 }else if(menu.events){ // menu instance
3523 /*else if(typeof menu.length == 'number'){ // array of menu items?
3524 return new Roo.bootstrap.Menu({items:menu});
3525 }else{ // otherwise, must be a config
3526 return new Roo.bootstrap.Menu(menu);
3533 unregister : function(menu){
3534 delete menus[menu.id];
3535 menu.un("beforehide", onBeforeHide);
3536 menu.un("hide", onHide);
3537 menu.un("beforeshow", onBeforeShow);
3538 menu.un("show", onShow);
3540 if(g && menu.events["checkchange"]){
3541 groups[g].remove(menu);
3542 menu.un("checkchange", onCheck);
3547 registerCheckable : function(menuItem){
3548 var g = menuItem.group;
3553 groups[g].push(menuItem);
3554 menuItem.on("beforecheckchange", onBeforeCheck);
3559 unregisterCheckable : function(menuItem){
3560 var g = menuItem.group;
3562 groups[g].remove(menuItem);
3563 menuItem.un("beforecheckchange", onBeforeCheck);
3575 * @class Roo.bootstrap.Menu
3576 * @extends Roo.bootstrap.Component
3577 * Bootstrap Menu class - container for MenuItems
3578 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579 * @cfg {bool} hidden if the menu should be hidden when rendered.
3580 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3581 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3582 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3583 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3587 * @param {Object} config The config object
3591 Roo.bootstrap.Menu = function(config){
3593 if (config.type == 'treeview') {
3594 // normally menu's are drawn attached to the document to handle layering etc..
3595 // however treeview (used by the docs menu is drawn into the parent element)
3596 this.container_method = 'getChildContainer';
3599 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3600 if (this.registerMenu && this.type != 'treeview') {
3601 Roo.bootstrap.MenuMgr.register(this);
3608 * Fires before this menu is displayed (return false to block)
3609 * @param {Roo.menu.Menu} this
3614 * Fires before this menu is hidden (return false to block)
3615 * @param {Roo.menu.Menu} this
3620 * Fires after this menu is displayed
3621 * @param {Roo.menu.Menu} this
3626 * Fires after this menu is hidden
3627 * @param {Roo.menu.Menu} this
3632 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3633 * @param {Roo.menu.Menu} this
3634 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3635 * @param {Roo.EventObject} e
3640 * Fires when the mouse is hovering over this menu
3641 * @param {Roo.menu.Menu} this
3642 * @param {Roo.EventObject} e
3643 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3648 * Fires when the mouse exits this menu
3649 * @param {Roo.menu.Menu} this
3650 * @param {Roo.EventObject} e
3651 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3656 * Fires when a menu item contained in this menu is clicked
3657 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3658 * @param {Roo.EventObject} e
3662 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3665 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3669 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3672 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3674 registerMenu : true,
3676 menuItems :false, // stores the menu items..
3686 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3688 hideTrigger : false,
3693 getChildContainer : function() {
3697 getAutoCreate : function(){
3699 //if (['right'].indexOf(this.align)!==-1) {
3700 // cfg.cn[1].cls += ' pull-right'
3705 cls : 'dropdown-menu shadow' ,
3706 style : 'z-index:1000'
3710 if (this.type === 'submenu') {
3711 cfg.cls = 'submenu active';
3713 if (this.type === 'treeview') {
3714 cfg.cls = 'treeview-menu';
3719 initEvents : function() {
3721 // Roo.log("ADD event");
3722 // Roo.log(this.triggerEl.dom);
3723 if (this.triggerEl) {
3725 this.triggerEl.on('click', this.onTriggerClick, this);
3727 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3729 if (!this.hideTrigger) {
3730 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3731 // dropdown toggle on the 'a' in BS4?
3732 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3734 this.triggerEl.addClass('dropdown-toggle');
3740 this.el.on('touchstart' , this.onTouch, this);
3742 this.el.on('click' , this.onClick, this);
3744 this.el.on("mouseover", this.onMouseOver, this);
3745 this.el.on("mouseout", this.onMouseOut, this);
3749 findTargetItem : function(e)
3751 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3755 //Roo.log(t); Roo.log(t.id);
3757 //Roo.log(this.menuitems);
3758 return this.menuitems.get(t.id);
3760 //return this.items.get(t.menuItemId);
3766 onTouch : function(e)
3768 Roo.log("menu.onTouch");
3769 //e.stopEvent(); this make the user popdown broken
3773 onClick : function(e)
3775 Roo.log("menu.onClick");
3777 var t = this.findTargetItem(e);
3778 if(!t || t.isContainer){
3783 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3784 if(t == this.activeItem && t.shouldDeactivate(e)){
3785 this.activeItem.deactivate();
3786 delete this.activeItem;
3790 this.setActiveItem(t, true);
3798 Roo.log('pass click event');
3802 this.fireEvent("click", this, t, e);
3806 if(!t.href.length || t.href == '#'){
3807 (function() { _this.hide(); }).defer(100);
3812 onMouseOver : function(e){
3813 var t = this.findTargetItem(e);
3816 // if(t.canActivate && !t.disabled){
3817 // this.setActiveItem(t, true);
3821 this.fireEvent("mouseover", this, e, t);
3823 isVisible : function(){
3824 return !this.hidden;
3826 onMouseOut : function(e){
3827 var t = this.findTargetItem(e);
3830 // if(t == this.activeItem && t.shouldDeactivate(e)){
3831 // this.activeItem.deactivate();
3832 // delete this.activeItem;
3835 this.fireEvent("mouseout", this, e, t);
3840 * Displays this menu relative to another element
3841 * @param {String/HTMLElement/Roo.Element} element The element to align to
3842 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3843 * the element (defaults to this.defaultAlign)
3844 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3846 show : function(el, pos, parentMenu)
3848 if (false === this.fireEvent("beforeshow", this)) {
3849 Roo.log("show canceled");
3852 this.parentMenu = parentMenu;
3856 this.el.addClass('show'); // show otherwise we do not know how big we are..
3858 var xy = this.el.getAlignToXY(el, pos);
3860 // bl-tl << left align below
3861 // tl-bl << left align
3863 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3864 // if it goes to far to the right.. -> align left.
3865 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3868 // was left align - go right?
3869 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3872 // goes down the bottom
3873 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3875 var a = this.align.replace('?', '').split('-');
3876 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3880 this.showAt( xy , parentMenu, false);
3883 * Displays this menu at a specific xy position
3884 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3885 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3887 showAt : function(xy, parentMenu, /* private: */_e){
3888 this.parentMenu = parentMenu;
3893 this.fireEvent("beforeshow", this);
3894 //xy = this.el.adjustForConstraints(xy);
3898 this.hideMenuItems();
3899 this.hidden = false;
3900 if (this.triggerEl) {
3901 this.triggerEl.addClass('open');
3904 this.el.addClass('show');
3908 // reassign x when hitting right
3910 // reassign y when hitting bottom
3912 // but the list may align on trigger left or trigger top... should it be a properity?
3914 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3919 this.fireEvent("show", this);
3925 this.doFocus.defer(50, this);
3929 doFocus : function(){
3931 this.focusEl.focus();
3936 * Hides this menu and optionally all parent menus
3937 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3939 hide : function(deep)
3941 if (false === this.fireEvent("beforehide", this)) {
3942 Roo.log("hide canceled");
3945 this.hideMenuItems();
3946 if(this.el && this.isVisible()){
3948 if(this.activeItem){
3949 this.activeItem.deactivate();
3950 this.activeItem = null;
3952 if (this.triggerEl) {
3953 this.triggerEl.removeClass('open');
3956 this.el.removeClass('show');
3958 this.fireEvent("hide", this);
3960 if(deep === true && this.parentMenu){
3961 this.parentMenu.hide(true);
3965 onTriggerClick : function(e)
3967 Roo.log('trigger click');
3969 var target = e.getTarget();
3971 Roo.log(target.nodeName.toLowerCase());
3973 if(target.nodeName.toLowerCase() === 'i'){
3979 onTriggerPress : function(e)
3981 Roo.log('trigger press');
3982 //Roo.log(e.getTarget());
3983 // Roo.log(this.triggerEl.dom);
3985 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3986 var pel = Roo.get(e.getTarget());
3987 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3988 Roo.log('is treeview or dropdown?');
3992 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3996 if (this.isVisible()) {
4002 this.show(this.triggerEl, this.align, false);
4005 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4012 hideMenuItems : function()
4014 Roo.log("hide Menu Items");
4019 this.el.select('.open',true).each(function(aa) {
4021 aa.removeClass('open');
4025 addxtypeChild : function (tree, cntr) {
4026 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4028 this.menuitems.add(comp);
4040 this.getEl().dom.innerHTML = '';
4041 this.menuitems.clear();
4055 * @class Roo.bootstrap.MenuItem
4056 * @extends Roo.bootstrap.Component
4057 * Bootstrap MenuItem class
4058 * @cfg {String} html the menu label
4059 * @cfg {String} href the link
4060 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4061 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4062 * @cfg {Boolean} active used on sidebars to highlight active itesm
4063 * @cfg {String} fa favicon to show on left of menu item.
4064 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4068 * Create a new MenuItem
4069 * @param {Object} config The config object
4073 Roo.bootstrap.MenuItem = function(config){
4074 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4079 * The raw click event for the entire grid.
4080 * @param {Roo.bootstrap.MenuItem} this
4081 * @param {Roo.EventObject} e
4087 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4091 preventDefault: false,
4092 isContainer : false,
4096 getAutoCreate : function(){
4098 if(this.isContainer){
4101 cls: 'dropdown-menu-item '
4111 cls : 'dropdown-item',
4116 if (this.fa !== false) {
4119 cls : 'fa fa-' + this.fa
4128 cls: 'dropdown-menu-item',
4131 if (this.parent().type == 'treeview') {
4132 cfg.cls = 'treeview-menu';
4135 cfg.cls += ' active';
4140 anc.href = this.href || cfg.cn[0].href ;
4141 ctag.html = this.html || cfg.cn[0].html ;
4145 initEvents: function()
4147 if (this.parent().type == 'treeview') {
4148 this.el.select('a').on('click', this.onClick, this);
4152 this.menu.parentType = this.xtype;
4153 this.menu.triggerEl = this.el;
4154 this.menu = this.addxtype(Roo.apply({}, this.menu));
4158 onClick : function(e)
4160 Roo.log('item on click ');
4162 if(this.preventDefault){
4165 //this.parent().hideMenuItems();
4167 this.fireEvent('click', this, e);
4186 * @class Roo.bootstrap.MenuSeparator
4187 * @extends Roo.bootstrap.Component
4188 * Bootstrap MenuSeparator class
4191 * Create a new MenuItem
4192 * @param {Object} config The config object
4196 Roo.bootstrap.MenuSeparator = function(config){
4197 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4200 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4202 getAutoCreate : function(){
4221 * @class Roo.bootstrap.Modal
4222 * @extends Roo.bootstrap.Component
4223 * Bootstrap Modal class
4224 * @cfg {String} title Title of dialog
4225 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4226 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4227 * @cfg {Boolean} specificTitle default false
4228 * @cfg {Array} buttons Array of buttons or standard button set..
4229 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4230 * @cfg {Boolean} animate default true
4231 * @cfg {Boolean} allow_close default true
4232 * @cfg {Boolean} fitwindow default false
4233 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4234 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4235 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4236 * @cfg {String} size (sm|lg|xl) default empty
4237 * @cfg {Number} max_width set the max width of modal
4238 * @cfg {Boolean} editableTitle can the title be edited
4243 * Create a new Modal Dialog
4244 * @param {Object} config The config object
4247 Roo.bootstrap.Modal = function(config){
4248 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4253 * The raw btnclick event for the button
4254 * @param {Roo.EventObject} e
4259 * Fire when dialog resize
4260 * @param {Roo.bootstrap.Modal} this
4261 * @param {Roo.EventObject} e
4265 * @event titlechanged
4266 * Fire when the editable title has been changed
4267 * @param {Roo.bootstrap.Modal} this
4268 * @param {Roo.EventObject} value
4270 "titlechanged" : true
4273 this.buttons = this.buttons || [];
4276 this.tmpl = Roo.factory(this.tmpl);
4281 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4283 title : 'test dialog',
4293 specificTitle: false,
4295 buttonPosition: 'right',
4317 editableTitle : false,
4319 onRender : function(ct, position)
4321 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4324 var cfg = Roo.apply({}, this.getAutoCreate());
4327 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4329 //if (!cfg.name.length) {
4333 cfg.cls += ' ' + this.cls;
4336 cfg.style = this.style;
4338 this.el = Roo.get(document.body).createChild(cfg, position);
4340 //var type = this.el.dom.type;
4343 if(this.tabIndex !== undefined){
4344 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4347 this.dialogEl = this.el.select('.modal-dialog',true).first();
4348 this.bodyEl = this.el.select('.modal-body',true).first();
4349 this.closeEl = this.el.select('.modal-header .close', true).first();
4350 this.headerEl = this.el.select('.modal-header',true).first();
4351 this.titleEl = this.el.select('.modal-title',true).first();
4352 this.footerEl = this.el.select('.modal-footer',true).first();
4354 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4356 //this.el.addClass("x-dlg-modal");
4358 if (this.buttons.length) {
4359 Roo.each(this.buttons, function(bb) {
4360 var b = Roo.apply({}, bb);
4361 b.xns = b.xns || Roo.bootstrap;
4362 b.xtype = b.xtype || 'Button';
4363 if (typeof(b.listeners) == 'undefined') {
4364 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4367 var btn = Roo.factory(b);
4369 btn.render(this.getButtonContainer());
4373 // render the children.
4376 if(typeof(this.items) != 'undefined'){
4377 var items = this.items;
4380 for(var i =0;i < items.length;i++) {
4381 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4385 this.items = nitems;
4387 // where are these used - they used to be body/close/footer
4391 //this.el.addClass([this.fieldClass, this.cls]);
4395 getAutoCreate : function()
4397 // we will default to modal-body-overflow - might need to remove or make optional later.
4399 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4400 html : this.html || ''
4405 cls : 'modal-title',
4409 if(this.specificTitle){ // WTF is this?
4414 if (this.allow_close && Roo.bootstrap.version == 3) {
4424 if (this.editableTitle) {
4426 cls: 'form-control roo-editable-title d-none',
4432 if (this.allow_close && Roo.bootstrap.version == 4) {
4442 if(this.size.length){
4443 size = 'modal-' + this.size;
4446 var footer = Roo.bootstrap.version == 3 ?
4448 cls : 'modal-footer',
4452 cls: 'btn-' + this.buttonPosition
4457 { // BS4 uses mr-auto on left buttons....
4458 cls : 'modal-footer'
4469 cls: "modal-dialog " + size,
4472 cls : "modal-content",
4475 cls : 'modal-header',
4490 modal.cls += ' fade';
4496 getChildContainer : function() {
4501 getButtonContainer : function() {
4503 return Roo.bootstrap.version == 4 ?
4504 this.el.select('.modal-footer',true).first()
4505 : this.el.select('.modal-footer div',true).first();
4508 initEvents : function()
4510 if (this.allow_close) {
4511 this.closeEl.on('click', this.hide, this);
4513 Roo.EventManager.onWindowResize(this.resize, this, true);
4514 if (this.editableTitle) {
4515 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4516 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4517 this.headerEditEl.on('keyup', function(e) {
4518 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4519 this.toggleHeaderInput(false)
4522 this.headerEditEl.on('blur', function(e) {
4523 this.toggleHeaderInput(false)
4532 this.maskEl.setSize(
4533 Roo.lib.Dom.getViewWidth(true),
4534 Roo.lib.Dom.getViewHeight(true)
4537 if (this.fitwindow) {
4539 this.dialogEl.setStyle( { 'max-width' : '100%' });
4541 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4542 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4547 if(this.max_width !== 0) {
4549 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4552 this.setSize(w, this.height);
4556 if(this.max_height) {
4557 this.setSize(w,Math.min(
4559 Roo.lib.Dom.getViewportHeight(true) - 60
4565 if(!this.fit_content) {
4566 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4570 this.setSize(w, Math.min(
4572 this.headerEl.getHeight() +
4573 this.footerEl.getHeight() +
4574 this.getChildHeight(this.bodyEl.dom.childNodes),
4575 Roo.lib.Dom.getViewportHeight(true) - 60)
4581 setSize : function(w,h)
4592 if (!this.rendered) {
4595 this.toggleHeaderInput(false);
4596 //this.el.setStyle('display', 'block');
4597 this.el.removeClass('hideing');
4598 this.el.dom.style.display='block';
4600 Roo.get(document.body).addClass('modal-open');
4602 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4605 this.el.addClass('show');
4606 this.el.addClass('in');
4609 this.el.addClass('show');
4610 this.el.addClass('in');
4613 // not sure how we can show data in here..
4615 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4618 Roo.get(document.body).addClass("x-body-masked");
4620 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4621 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4622 this.maskEl.dom.style.display = 'block';
4623 this.maskEl.addClass('show');
4628 this.fireEvent('show', this);
4630 // set zindex here - otherwise it appears to be ignored...
4631 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4634 this.items.forEach( function(e) {
4635 e.layout ? e.layout() : false;
4643 if(this.fireEvent("beforehide", this) !== false){
4645 this.maskEl.removeClass('show');
4647 this.maskEl.dom.style.display = '';
4648 Roo.get(document.body).removeClass("x-body-masked");
4649 this.el.removeClass('in');
4650 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4652 if(this.animate){ // why
4653 this.el.addClass('hideing');
4654 this.el.removeClass('show');
4656 if (!this.el.hasClass('hideing')) {
4657 return; // it's been shown again...
4660 this.el.dom.style.display='';
4662 Roo.get(document.body).removeClass('modal-open');
4663 this.el.removeClass('hideing');
4667 this.el.removeClass('show');
4668 this.el.dom.style.display='';
4669 Roo.get(document.body).removeClass('modal-open');
4672 this.fireEvent('hide', this);
4675 isVisible : function()
4678 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4682 addButton : function(str, cb)
4686 var b = Roo.apply({}, { html : str } );
4687 b.xns = b.xns || Roo.bootstrap;
4688 b.xtype = b.xtype || 'Button';
4689 if (typeof(b.listeners) == 'undefined') {
4690 b.listeners = { click : cb.createDelegate(this) };
4693 var btn = Roo.factory(b);
4695 btn.render(this.getButtonContainer());
4701 setDefaultButton : function(btn)
4703 //this.el.select('.modal-footer').()
4706 resizeTo: function(w,h)
4708 this.dialogEl.setWidth(w);
4710 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4712 this.bodyEl.setHeight(h - diff);
4714 this.fireEvent('resize', this);
4717 setContentSize : function(w, h)
4721 onButtonClick: function(btn,e)
4724 this.fireEvent('btnclick', btn.name, e);
4727 * Set the title of the Dialog
4728 * @param {String} str new Title
4730 setTitle: function(str) {
4731 this.titleEl.dom.innerHTML = str;
4735 * Set the body of the Dialog
4736 * @param {String} str new Title
4738 setBody: function(str) {
4739 this.bodyEl.dom.innerHTML = str;
4742 * Set the body of the Dialog using the template
4743 * @param {Obj} data - apply this data to the template and replace the body contents.
4745 applyBody: function(obj)
4748 Roo.log("Error - using apply Body without a template");
4751 this.tmpl.overwrite(this.bodyEl, obj);
4754 getChildHeight : function(child_nodes)
4758 child_nodes.length == 0
4763 var child_height = 0;
4765 for(var i = 0; i < child_nodes.length; i++) {
4768 * for modal with tabs...
4769 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4771 var layout_childs = child_nodes[i].childNodes;
4773 for(var j = 0; j < layout_childs.length; j++) {
4775 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4777 var layout_body_childs = layout_childs[j].childNodes;
4779 for(var k = 0; k < layout_body_childs.length; k++) {
4781 if(layout_body_childs[k].classList.contains('navbar')) {
4782 child_height += layout_body_childs[k].offsetHeight;
4786 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4788 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4790 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4792 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4793 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4808 child_height += child_nodes[i].offsetHeight;
4809 // Roo.log(child_nodes[i].offsetHeight);
4812 return child_height;
4814 toggleHeaderInput : function(is_edit)
4816 if (!this.editableTitle) {
4817 return; // not editable.
4819 if (is_edit && this.is_header_editing) {
4820 return; // already editing..
4824 this.headerEditEl.dom.value = this.title;
4825 this.headerEditEl.removeClass('d-none');
4826 this.headerEditEl.dom.focus();
4827 this.titleEl.addClass('d-none');
4829 this.is_header_editing = true;
4832 // flip back to not editing.
4833 this.title = this.headerEditEl.dom.value;
4834 this.headerEditEl.addClass('d-none');
4835 this.titleEl.removeClass('d-none');
4836 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4837 this.is_header_editing = false;
4838 this.fireEvent('titlechanged', this, this.title);
4847 Roo.apply(Roo.bootstrap.Modal, {
4849 * Button config that displays a single OK button
4858 * Button config that displays Yes and No buttons
4874 * Button config that displays OK and Cancel buttons
4889 * Button config that displays Yes, No and Cancel buttons
4914 * messagebox - can be used as a replace
4918 * @class Roo.MessageBox
4919 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4923 Roo.Msg.alert('Status', 'Changes saved successfully.');
4925 // Prompt for user data:
4926 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4928 // process text value...
4932 // Show a dialog using config options:
4934 title:'Save Changes?',
4935 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4936 buttons: Roo.Msg.YESNOCANCEL,
4943 Roo.bootstrap.MessageBox = function(){
4944 var dlg, opt, mask, waitTimer;
4945 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4946 var buttons, activeTextEl, bwidth;
4950 var handleButton = function(button){
4952 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4956 var handleHide = function(){
4958 dlg.el.removeClass(opt.cls);
4961 // Roo.TaskMgr.stop(waitTimer);
4962 // waitTimer = null;
4967 var updateButtons = function(b){
4970 buttons["ok"].hide();
4971 buttons["cancel"].hide();
4972 buttons["yes"].hide();
4973 buttons["no"].hide();
4974 dlg.footerEl.hide();
4978 dlg.footerEl.show();
4979 for(var k in buttons){
4980 if(typeof buttons[k] != "function"){
4983 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4984 width += buttons[k].el.getWidth()+15;
4994 var handleEsc = function(d, k, e){
4995 if(opt && opt.closable !== false){
5005 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5006 * @return {Roo.BasicDialog} The BasicDialog element
5008 getDialog : function(){
5010 dlg = new Roo.bootstrap.Modal( {
5013 //constraintoviewport:false,
5015 //collapsible : false,
5020 //buttonAlign:"center",
5021 closeClick : function(){
5022 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5025 handleButton("cancel");
5030 dlg.on("hide", handleHide);
5032 //dlg.addKeyListener(27, handleEsc);
5034 this.buttons = buttons;
5035 var bt = this.buttonText;
5036 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5037 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5038 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5039 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5041 bodyEl = dlg.bodyEl.createChild({
5043 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5044 '<textarea class="roo-mb-textarea"></textarea>' +
5045 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5047 msgEl = bodyEl.dom.firstChild;
5048 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5049 textboxEl.enableDisplayMode();
5050 textboxEl.addKeyListener([10,13], function(){
5051 if(dlg.isVisible() && opt && opt.buttons){
5054 }else if(opt.buttons.yes){
5055 handleButton("yes");
5059 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5060 textareaEl.enableDisplayMode();
5061 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5062 progressEl.enableDisplayMode();
5064 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5065 var pf = progressEl.dom.firstChild;
5067 pp = Roo.get(pf.firstChild);
5068 pp.setHeight(pf.offsetHeight);
5076 * Updates the message box body text
5077 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5078 * the XHTML-compliant non-breaking space character '&#160;')
5079 * @return {Roo.MessageBox} This message box
5081 updateText : function(text)
5083 if(!dlg.isVisible() && !opt.width){
5084 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5085 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5087 msgEl.innerHTML = text || ' ';
5089 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5090 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5092 Math.min(opt.width || cw , this.maxWidth),
5093 Math.max(opt.minWidth || this.minWidth, bwidth)
5096 activeTextEl.setWidth(w);
5098 if(dlg.isVisible()){
5099 dlg.fixedcenter = false;
5101 // to big, make it scroll. = But as usual stupid IE does not support
5104 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5105 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5106 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5108 bodyEl.dom.style.height = '';
5109 bodyEl.dom.style.overflowY = '';
5112 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5114 bodyEl.dom.style.overflowX = '';
5117 dlg.setContentSize(w, bodyEl.getHeight());
5118 if(dlg.isVisible()){
5119 dlg.fixedcenter = true;
5125 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5126 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5127 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5128 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5129 * @return {Roo.MessageBox} This message box
5131 updateProgress : function(value, text){
5133 this.updateText(text);
5136 if (pp) { // weird bug on my firefox - for some reason this is not defined
5137 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5138 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5144 * Returns true if the message box is currently displayed
5145 * @return {Boolean} True if the message box is visible, else false
5147 isVisible : function(){
5148 return dlg && dlg.isVisible();
5152 * Hides the message box if it is displayed
5155 if(this.isVisible()){
5161 * Displays a new message box, or reinitializes an existing message box, based on the config options
5162 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5163 * The following config object properties are supported:
5165 Property Type Description
5166 ---------- --------------- ------------------------------------------------------------------------------------
5167 animEl String/Element An id or Element from which the message box should animate as it opens and
5168 closes (defaults to undefined)
5169 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5170 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5171 closable Boolean False to hide the top-right close button (defaults to true). Note that
5172 progress and wait dialogs will ignore this property and always hide the
5173 close button as they can only be closed programmatically.
5174 cls String A custom CSS class to apply to the message box element
5175 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5176 displayed (defaults to 75)
5177 fn Function A callback function to execute after closing the dialog. The arguments to the
5178 function will be btn (the name of the button that was clicked, if applicable,
5179 e.g. "ok"), and text (the value of the active text field, if applicable).
5180 Progress and wait dialogs will ignore this option since they do not respond to
5181 user actions and can only be closed programmatically, so any required function
5182 should be called by the same code after it closes the dialog.
5183 icon String A CSS class that provides a background image to be used as an icon for
5184 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5185 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5186 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5187 modal Boolean False to allow user interaction with the page while the message box is
5188 displayed (defaults to true)
5189 msg String A string that will replace the existing message box body text (defaults
5190 to the XHTML-compliant non-breaking space character ' ')
5191 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5192 progress Boolean True to display a progress bar (defaults to false)
5193 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5194 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5195 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5196 title String The title text
5197 value String The string value to set into the active textbox element if displayed
5198 wait Boolean True to display a progress bar (defaults to false)
5199 width Number The width of the dialog in pixels
5206 msg: 'Please enter your address:',
5208 buttons: Roo.MessageBox.OKCANCEL,
5211 animEl: 'addAddressBtn'
5214 * @param {Object} config Configuration options
5215 * @return {Roo.MessageBox} This message box
5217 show : function(options)
5220 // this causes nightmares if you show one dialog after another
5221 // especially on callbacks..
5223 if(this.isVisible()){
5226 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5227 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5228 Roo.log("New Dialog Message:" + options.msg )
5229 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5230 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5233 var d = this.getDialog();
5235 d.setTitle(opt.title || " ");
5236 d.closeEl.setDisplayed(opt.closable !== false);
5237 activeTextEl = textboxEl;
5238 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5243 textareaEl.setHeight(typeof opt.multiline == "number" ?
5244 opt.multiline : this.defaultTextHeight);
5245 activeTextEl = textareaEl;
5254 progressEl.setDisplayed(opt.progress === true);
5256 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5258 this.updateProgress(0);
5259 activeTextEl.dom.value = opt.value || "";
5261 dlg.setDefaultButton(activeTextEl);
5263 var bs = opt.buttons;
5267 }else if(bs && bs.yes){
5268 db = buttons["yes"];
5270 dlg.setDefaultButton(db);
5272 bwidth = updateButtons(opt.buttons);
5273 this.updateText(opt.msg);
5275 d.el.addClass(opt.cls);
5277 d.proxyDrag = opt.proxyDrag === true;
5278 d.modal = opt.modal !== false;
5279 d.mask = opt.modal !== false ? mask : false;
5281 // force it to the end of the z-index stack so it gets a cursor in FF
5282 document.body.appendChild(dlg.el.dom);
5283 d.animateTarget = null;
5284 d.show(options.animEl);
5290 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5291 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5292 * and closing the message box when the process is complete.
5293 * @param {String} title The title bar text
5294 * @param {String} msg The message box body text
5295 * @return {Roo.MessageBox} This message box
5297 progress : function(title, msg){
5304 minWidth: this.minProgressWidth,
5311 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5312 * If a callback function is passed it will be called after the user clicks the button, and the
5313 * id of the button that was clicked will be passed as the only parameter to the callback
5314 * (could also be the top-right close button).
5315 * @param {String} title The title bar text
5316 * @param {String} msg The message box body text
5317 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5318 * @param {Object} scope (optional) The scope of the callback function
5319 * @return {Roo.MessageBox} This message box
5321 alert : function(title, msg, fn, scope)
5336 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5337 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5338 * You are responsible for closing the message box when the process is complete.
5339 * @param {String} msg The message box body text
5340 * @param {String} title (optional) The title bar text
5341 * @return {Roo.MessageBox} This message box
5343 wait : function(msg, title){
5354 waitTimer = Roo.TaskMgr.start({
5356 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5364 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5365 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5366 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5367 * @param {String} title The title bar text
5368 * @param {String} msg The message box body text
5369 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5370 * @param {Object} scope (optional) The scope of the callback function
5371 * @return {Roo.MessageBox} This message box
5373 confirm : function(title, msg, fn, scope){
5377 buttons: this.YESNO,
5386 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5387 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5388 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5389 * (could also be the top-right close button) and the text that was entered will be passed as the two
5390 * parameters to the callback.
5391 * @param {String} title The title bar text
5392 * @param {String} msg The message box body text
5393 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5394 * @param {Object} scope (optional) The scope of the callback function
5395 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5396 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5397 * @return {Roo.MessageBox} This message box
5399 prompt : function(title, msg, fn, scope, multiline){
5403 buttons: this.OKCANCEL,
5408 multiline: multiline,
5415 * Button config that displays a single OK button
5420 * Button config that displays Yes and No buttons
5423 YESNO : {yes:true, no:true},
5425 * Button config that displays OK and Cancel buttons
5428 OKCANCEL : {ok:true, cancel:true},
5430 * Button config that displays Yes, No and Cancel buttons
5433 YESNOCANCEL : {yes:true, no:true, cancel:true},
5436 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5439 defaultTextHeight : 75,
5441 * The maximum width in pixels of the message box (defaults to 600)
5446 * The minimum width in pixels of the message box (defaults to 100)
5451 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5452 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5455 minProgressWidth : 250,
5457 * An object containing the default button text strings that can be overriden for localized language support.
5458 * Supported properties are: ok, cancel, yes and no.
5459 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5472 * Shorthand for {@link Roo.MessageBox}
5474 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5475 Roo.Msg = Roo.Msg || Roo.MessageBox;
5484 * @class Roo.bootstrap.Navbar
5485 * @extends Roo.bootstrap.Component
5486 * Bootstrap Navbar class
5489 * Create a new Navbar
5490 * @param {Object} config The config object
5494 Roo.bootstrap.Navbar = function(config){
5495 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5499 * @event beforetoggle
5500 * Fire before toggle the menu
5501 * @param {Roo.EventObject} e
5503 "beforetoggle" : true
5507 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5516 getAutoCreate : function(){
5519 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5523 initEvents :function ()
5525 //Roo.log(this.el.select('.navbar-toggle',true));
5526 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5533 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5535 var size = this.el.getSize();
5536 this.maskEl.setSize(size.width, size.height);
5537 this.maskEl.enableDisplayMode("block");
5546 getChildContainer : function()
5548 if (this.el && this.el.select('.collapse').getCount()) {
5549 return this.el.select('.collapse',true).first();
5564 onToggle : function()
5567 if(this.fireEvent('beforetoggle', this) === false){
5570 var ce = this.el.select('.navbar-collapse',true).first();
5572 if (!ce.hasClass('show')) {
5582 * Expand the navbar pulldown
5584 expand : function ()
5587 var ce = this.el.select('.navbar-collapse',true).first();
5588 if (ce.hasClass('collapsing')) {
5591 ce.dom.style.height = '';
5593 ce.addClass('in'); // old...
5594 ce.removeClass('collapse');
5595 ce.addClass('show');
5596 var h = ce.getHeight();
5598 ce.removeClass('show');
5599 // at this point we should be able to see it..
5600 ce.addClass('collapsing');
5602 ce.setHeight(0); // resize it ...
5603 ce.on('transitionend', function() {
5604 //Roo.log('done transition');
5605 ce.removeClass('collapsing');
5606 ce.addClass('show');
5607 ce.removeClass('collapse');
5609 ce.dom.style.height = '';
5610 }, this, { single: true} );
5612 ce.dom.scrollTop = 0;
5615 * Collapse the navbar pulldown
5617 collapse : function()
5619 var ce = this.el.select('.navbar-collapse',true).first();
5621 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5622 // it's collapsed or collapsing..
5625 ce.removeClass('in'); // old...
5626 ce.setHeight(ce.getHeight());
5627 ce.removeClass('show');
5628 ce.addClass('collapsing');
5630 ce.on('transitionend', function() {
5631 ce.dom.style.height = '';
5632 ce.removeClass('collapsing');
5633 ce.addClass('collapse');
5634 }, this, { single: true} );
5654 * @class Roo.bootstrap.NavSimplebar
5655 * @extends Roo.bootstrap.Navbar
5656 * Bootstrap Sidebar class
5658 * @cfg {Boolean} inverse is inverted color
5660 * @cfg {String} type (nav | pills | tabs)
5661 * @cfg {Boolean} arrangement stacked | justified
5662 * @cfg {String} align (left | right) alignment
5664 * @cfg {Boolean} main (true|false) main nav bar? default false
5665 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5667 * @cfg {String} tag (header|footer|nav|div) default is nav
5669 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5673 * Create a new Sidebar
5674 * @param {Object} config The config object
5678 Roo.bootstrap.NavSimplebar = function(config){
5679 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5682 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5698 getAutoCreate : function(){
5702 tag : this.tag || 'div',
5703 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5705 if (['light','white'].indexOf(this.weight) > -1) {
5706 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5708 cfg.cls += ' bg-' + this.weight;
5711 cfg.cls += ' navbar-inverse';
5715 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5717 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5726 cls: 'nav nav-' + this.xtype,
5732 this.type = this.type || 'nav';
5733 if (['tabs','pills'].indexOf(this.type) != -1) {
5734 cfg.cn[0].cls += ' nav-' + this.type
5738 if (this.type!=='nav') {
5739 Roo.log('nav type must be nav/tabs/pills')
5741 cfg.cn[0].cls += ' navbar-nav'
5747 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5748 cfg.cn[0].cls += ' nav-' + this.arrangement;
5752 if (this.align === 'right') {
5753 cfg.cn[0].cls += ' navbar-right';
5778 * navbar-expand-md fixed-top
5782 * @class Roo.bootstrap.NavHeaderbar
5783 * @extends Roo.bootstrap.NavSimplebar
5784 * Bootstrap Sidebar class
5786 * @cfg {String} brand what is brand
5787 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5788 * @cfg {String} brand_href href of the brand
5789 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5790 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5791 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5792 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5795 * Create a new Sidebar
5796 * @param {Object} config The config object
5800 Roo.bootstrap.NavHeaderbar = function(config){
5801 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5805 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5812 desktopCenter : false,
5815 getAutoCreate : function(){
5818 tag: this.nav || 'nav',
5819 cls: 'navbar navbar-expand-md',
5825 if (this.desktopCenter) {
5826 cn.push({cls : 'container', cn : []});
5834 cls: 'navbar-toggle navbar-toggler',
5835 'data-toggle': 'collapse',
5840 html: 'Toggle navigation'
5844 cls: 'icon-bar navbar-toggler-icon'
5857 cn.push( Roo.bootstrap.version == 4 ? btn : {
5859 cls: 'navbar-header',
5868 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5872 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5874 if (['light','white'].indexOf(this.weight) > -1) {
5875 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5877 cfg.cls += ' bg-' + this.weight;
5880 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5881 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5883 // tag can override this..
5885 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5888 if (this.brand !== '') {
5889 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5890 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5892 href: this.brand_href ? this.brand_href : '#',
5893 cls: 'navbar-brand',
5901 cfg.cls += ' main-nav';
5909 getHeaderChildContainer : function()
5911 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5912 return this.el.select('.navbar-header',true).first();
5915 return this.getChildContainer();
5918 getChildContainer : function()
5921 return this.el.select('.roo-navbar-collapse',true).first();
5926 initEvents : function()
5928 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5930 if (this.autohide) {
5935 Roo.get(document).on('scroll',function(e) {
5936 var ns = Roo.get(document).getScroll().top;
5937 var os = prevScroll;
5941 ft.removeClass('slideDown');
5942 ft.addClass('slideUp');
5945 ft.removeClass('slideUp');
5946 ft.addClass('slideDown');
5967 * @class Roo.bootstrap.NavSidebar
5968 * @extends Roo.bootstrap.Navbar
5969 * Bootstrap Sidebar class
5972 * Create a new Sidebar
5973 * @param {Object} config The config object
5977 Roo.bootstrap.NavSidebar = function(config){
5978 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5981 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5983 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5985 getAutoCreate : function(){
5990 cls: 'sidebar sidebar-nav'
6012 * @class Roo.bootstrap.NavGroup
6013 * @extends Roo.bootstrap.Component
6014 * Bootstrap NavGroup class
6015 * @cfg {String} align (left|right)
6016 * @cfg {Boolean} inverse
6017 * @cfg {String} type (nav|pills|tab) default nav
6018 * @cfg {String} navId - reference Id for navbar.
6019 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6022 * Create a new nav group
6023 * @param {Object} config The config object
6026 Roo.bootstrap.NavGroup = function(config){
6027 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6030 Roo.bootstrap.NavGroup.register(this);
6034 * Fires when the active item changes
6035 * @param {Roo.bootstrap.NavGroup} this
6036 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6037 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6044 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6056 getAutoCreate : function()
6058 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6064 if (Roo.bootstrap.version == 4) {
6065 if (['tabs','pills'].indexOf(this.type) != -1) {
6066 cfg.cls += ' nav-' + this.type;
6068 // trying to remove so header bar can right align top?
6069 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6070 // do not use on header bar...
6071 cfg.cls += ' navbar-nav';
6076 if (['tabs','pills'].indexOf(this.type) != -1) {
6077 cfg.cls += ' nav-' + this.type
6079 if (this.type !== 'nav') {
6080 Roo.log('nav type must be nav/tabs/pills')
6082 cfg.cls += ' navbar-nav'
6086 if (this.parent() && this.parent().sidebar) {
6089 cls: 'dashboard-menu sidebar-menu'
6095 if (this.form === true) {
6098 cls: 'navbar-form form-inline'
6100 //nav navbar-right ml-md-auto
6101 if (this.align === 'right') {
6102 cfg.cls += ' navbar-right ml-md-auto';
6104 cfg.cls += ' navbar-left';
6108 if (this.align === 'right') {
6109 cfg.cls += ' navbar-right ml-md-auto';
6111 cfg.cls += ' mr-auto';
6115 cfg.cls += ' navbar-inverse';
6123 * sets the active Navigation item
6124 * @param {Roo.bootstrap.NavItem} the new current navitem
6126 setActiveItem : function(item)
6129 Roo.each(this.navItems, function(v){
6134 v.setActive(false, true);
6141 item.setActive(true, true);
6142 this.fireEvent('changed', this, item, prev);
6147 * gets the active Navigation item
6148 * @return {Roo.bootstrap.NavItem} the current navitem
6150 getActive : function()
6154 Roo.each(this.navItems, function(v){
6165 indexOfNav : function()
6169 Roo.each(this.navItems, function(v,i){
6180 * adds a Navigation item
6181 * @param {Roo.bootstrap.NavItem} the navitem to add
6183 addItem : function(cfg)
6185 if (this.form && Roo.bootstrap.version == 4) {
6188 var cn = new Roo.bootstrap.NavItem(cfg);
6190 cn.parentId = this.id;
6191 cn.onRender(this.el, null);
6195 * register a Navigation item
6196 * @param {Roo.bootstrap.NavItem} the navitem to add
6198 register : function(item)
6200 this.navItems.push( item);
6201 item.navId = this.navId;
6206 * clear all the Navigation item
6209 clearAll : function()
6212 this.el.dom.innerHTML = '';
6215 getNavItem: function(tabId)
6218 Roo.each(this.navItems, function(e) {
6219 if (e.tabId == tabId) {
6229 setActiveNext : function()
6231 var i = this.indexOfNav(this.getActive());
6232 if (i > this.navItems.length) {
6235 this.setActiveItem(this.navItems[i+1]);
6237 setActivePrev : function()
6239 var i = this.indexOfNav(this.getActive());
6243 this.setActiveItem(this.navItems[i-1]);
6245 clearWasActive : function(except) {
6246 Roo.each(this.navItems, function(e) {
6247 if (e.tabId != except.tabId && e.was_active) {
6248 e.was_active = false;
6255 getWasActive : function ()
6258 Roo.each(this.navItems, function(e) {
6273 Roo.apply(Roo.bootstrap.NavGroup, {
6277 * register a Navigation Group
6278 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6280 register : function(navgrp)
6282 this.groups[navgrp.navId] = navgrp;
6286 * fetch a Navigation Group based on the navigation ID
6287 * @param {string} the navgroup to add
6288 * @returns {Roo.bootstrap.NavGroup} the navgroup
6290 get: function(navId) {
6291 if (typeof(this.groups[navId]) == 'undefined') {
6293 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6295 return this.groups[navId] ;
6310 * @class Roo.bootstrap.NavItem
6311 * @extends Roo.bootstrap.Component
6312 * Bootstrap Navbar.NavItem class
6313 * @cfg {String} href link to
6314 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6315 * @cfg {Boolean} button_outline show and outlined button
6316 * @cfg {String} html content of button
6317 * @cfg {String} badge text inside badge
6318 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6319 * @cfg {String} glyphicon DEPRICATED - use fa
6320 * @cfg {String} icon DEPRICATED - use fa
6321 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6322 * @cfg {Boolean} active Is item active
6323 * @cfg {Boolean} disabled Is item disabled
6324 * @cfg {String} linkcls Link Class
6325 * @cfg {Boolean} preventDefault (true | false) default false
6326 * @cfg {String} tabId the tab that this item activates.
6327 * @cfg {String} tagtype (a|span) render as a href or span?
6328 * @cfg {Boolean} animateRef (true|false) link to element default false
6331 * Create a new Navbar Item
6332 * @param {Object} config The config object
6334 Roo.bootstrap.NavItem = function(config){
6335 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6340 * The raw click event for the entire grid.
6341 * @param {Roo.EventObject} e
6346 * Fires when the active item active state changes
6347 * @param {Roo.bootstrap.NavItem} this
6348 * @param {boolean} state the new state
6354 * Fires when scroll to element
6355 * @param {Roo.bootstrap.NavItem} this
6356 * @param {Object} options
6357 * @param {Roo.EventObject} e
6365 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6374 preventDefault : false,
6382 button_outline : false,
6386 getAutoCreate : function(){
6393 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6396 cfg.cls += ' active' ;
6398 if (this.disabled) {
6399 cfg.cls += ' disabled';
6403 if (this.button_weight.length) {
6404 cfg.tag = this.href ? 'a' : 'button';
6405 cfg.html = this.html || '';
6406 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6408 cfg.href = this.href;
6411 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6413 cfg.cls += " nav-html";
6416 // menu .. should add dropdown-menu class - so no need for carat..
6418 if (this.badge !== '') {
6420 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6425 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6429 href : this.href || "#",
6430 html: this.html || '',
6434 if (this.tagtype == 'a') {
6435 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6439 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6440 } else if (this.fa) {
6441 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6442 } else if(this.glyphicon) {
6443 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6445 cfg.cn[0].cls += " nav-html";
6449 cfg.cn[0].html += " <span class='caret'></span>";
6453 if (this.badge !== '') {
6454 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6462 onRender : function(ct, position)
6464 // Roo.log("Call onRender: " + this.xtype);
6465 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6469 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6470 this.navLink = this.el.select('.nav-link',true).first();
6471 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6476 initEvents: function()
6478 if (typeof (this.menu) != 'undefined') {
6479 this.menu.parentType = this.xtype;
6480 this.menu.triggerEl = this.el;
6481 this.menu = this.addxtype(Roo.apply({}, this.menu));
6484 this.el.on('click', this.onClick, this);
6486 //if(this.tagtype == 'span'){
6487 // this.el.select('span',true).on('click', this.onClick, this);
6490 // at this point parent should be available..
6491 this.parent().register(this);
6494 onClick : function(e)
6496 if (e.getTarget('.dropdown-menu-item')) {
6497 // did you click on a menu itemm.... - then don't trigger onclick..
6502 this.preventDefault ||
6505 Roo.log("NavItem - prevent Default?");
6509 if (this.disabled) {
6513 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6514 if (tg && tg.transition) {
6515 Roo.log("waiting for the transitionend");
6521 //Roo.log("fire event clicked");
6522 if(this.fireEvent('click', this, e) === false){
6526 if(this.tagtype == 'span'){
6530 //Roo.log(this.href);
6531 var ael = this.el.select('a',true).first();
6534 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6535 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6536 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6537 return; // ignore... - it's a 'hash' to another page.
6539 Roo.log("NavItem - prevent Default?");
6541 this.scrollToElement(e);
6545 var p = this.parent();
6547 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6548 if (typeof(p.setActiveItem) !== 'undefined') {
6549 p.setActiveItem(this);
6553 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6554 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6555 // remove the collapsed menu expand...
6556 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6560 isActive: function () {
6563 setActive : function(state, fire, is_was_active)
6565 if (this.active && !state && this.navId) {
6566 this.was_active = true;
6567 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6569 nv.clearWasActive(this);
6573 this.active = state;
6576 this.el.removeClass('active');
6577 this.navLink ? this.navLink.removeClass('active') : false;
6578 } else if (!this.el.hasClass('active')) {
6580 this.el.addClass('active');
6581 if (Roo.bootstrap.version == 4 && this.navLink ) {
6582 this.navLink.addClass('active');
6587 this.fireEvent('changed', this, state);
6590 // show a panel if it's registered and related..
6592 if (!this.navId || !this.tabId || !state || is_was_active) {
6596 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6600 var pan = tg.getPanelByName(this.tabId);
6604 // if we can not flip to new panel - go back to old nav highlight..
6605 if (false == tg.showPanel(pan)) {
6606 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6608 var onav = nv.getWasActive();
6610 onav.setActive(true, false, true);
6619 // this should not be here...
6620 setDisabled : function(state)
6622 this.disabled = state;
6624 this.el.removeClass('disabled');
6625 } else if (!this.el.hasClass('disabled')) {
6626 this.el.addClass('disabled');
6632 * Fetch the element to display the tooltip on.
6633 * @return {Roo.Element} defaults to this.el
6635 tooltipEl : function()
6637 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6640 scrollToElement : function(e)
6642 var c = document.body;
6645 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6647 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6648 c = document.documentElement;
6651 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6657 var o = target.calcOffsetsTo(c);
6664 this.fireEvent('scrollto', this, options, e);
6666 Roo.get(c).scrollTo('top', options.value, true);
6671 * Set the HTML (text content) of the item
6672 * @param {string} html content for the nav item
6674 setHtml : function(html)
6677 this.htmlEl.dom.innerHTML = html;
6689 * <span> icon </span>
6690 * <span> text </span>
6691 * <span>badge </span>
6695 * @class Roo.bootstrap.NavSidebarItem
6696 * @extends Roo.bootstrap.NavItem
6697 * Bootstrap Navbar.NavSidebarItem class
6698 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6699 * {Boolean} open is the menu open
6700 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6701 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6702 * {String} buttonSize (sm|md|lg)the extra classes for the button
6703 * {Boolean} showArrow show arrow next to the text (default true)
6705 * Create a new Navbar Button
6706 * @param {Object} config The config object
6708 Roo.bootstrap.NavSidebarItem = function(config){
6709 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6714 * The raw click event for the entire grid.
6715 * @param {Roo.EventObject} e
6720 * Fires when the active item active state changes
6721 * @param {Roo.bootstrap.NavSidebarItem} this
6722 * @param {boolean} state the new state
6730 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6732 badgeWeight : 'default',
6738 buttonWeight : 'default',
6744 getAutoCreate : function(){
6749 href : this.href || '#',
6755 if(this.buttonView){
6758 href : this.href || '#',
6759 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6772 cfg.cls += ' active';
6775 if (this.disabled) {
6776 cfg.cls += ' disabled';
6779 cfg.cls += ' open x-open';
6782 if (this.glyphicon || this.icon) {
6783 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6784 a.cn.push({ tag : 'i', cls : c }) ;
6787 if(!this.buttonView){
6790 html : this.html || ''
6797 if (this.badge !== '') {
6798 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6804 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6807 a.cls += ' dropdown-toggle treeview' ;
6813 initEvents : function()
6815 if (typeof (this.menu) != 'undefined') {
6816 this.menu.parentType = this.xtype;
6817 this.menu.triggerEl = this.el;
6818 this.menu = this.addxtype(Roo.apply({}, this.menu));
6821 this.el.on('click', this.onClick, this);
6823 if(this.badge !== ''){
6824 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6829 onClick : function(e)
6836 if(this.preventDefault){
6840 this.fireEvent('click', this, e);
6843 disable : function()
6845 this.setDisabled(true);
6850 this.setDisabled(false);
6853 setDisabled : function(state)
6855 if(this.disabled == state){
6859 this.disabled = state;
6862 this.el.addClass('disabled');
6866 this.el.removeClass('disabled');
6871 setActive : function(state)
6873 if(this.active == state){
6877 this.active = state;
6880 this.el.addClass('active');
6884 this.el.removeClass('active');
6889 isActive: function ()
6894 setBadge : function(str)
6900 this.badgeEl.dom.innerHTML = str;
6915 Roo.namespace('Roo.bootstrap.breadcrumb');
6919 * @class Roo.bootstrap.breadcrumb.Nav
6920 * @extends Roo.bootstrap.Component
6921 * Bootstrap Breadcrumb Nav Class
6923 * @children Roo.bootstrap.breadcrumb.Item
6926 * Create a new breadcrumb.Nav
6927 * @param {Object} config The config object
6931 Roo.bootstrap.breadcrumb.Nav = function(config){
6932 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6937 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6939 getAutoCreate : function()
6956 initEvents: function()
6958 this.olEl = this.el.select('ol',true).first();
6960 getChildContainer : function()
6976 * @class Roo.bootstrap.breadcrumb.Nav
6977 * @extends Roo.bootstrap.Component
6978 * Bootstrap Breadcrumb Nav Class
6980 * @children Roo.bootstrap.breadcrumb.Component
6981 * @cfg {String} html the content of the link.
6982 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6983 * @cfg {Boolean} active is it active
6987 * Create a new breadcrumb.Nav
6988 * @param {Object} config The config object
6991 Roo.bootstrap.breadcrumb.Item = function(config){
6992 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6997 * The img click event for the img.
6998 * @param {Roo.EventObject} e
7005 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7010 getAutoCreate : function()
7015 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7017 if (this.href !== false) {
7024 cfg.html = this.html;
7030 initEvents: function()
7033 this.el.select('a', true).first().on('click',this.onClick, this)
7037 onClick : function(e)
7040 this.fireEvent('click',this, e);
7053 * @class Roo.bootstrap.Row
7054 * @extends Roo.bootstrap.Component
7055 * Bootstrap Row class (contains columns...)
7059 * @param {Object} config The config object
7062 Roo.bootstrap.Row = function(config){
7063 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7066 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7068 getAutoCreate : function(){
7087 * @class Roo.bootstrap.Pagination
7088 * @extends Roo.bootstrap.Component
7089 * Bootstrap Pagination class
7090 * @cfg {String} size xs | sm | md | lg
7091 * @cfg {Boolean} inverse false | true
7094 * Create a new Pagination
7095 * @param {Object} config The config object
7098 Roo.bootstrap.Pagination = function(config){
7099 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7102 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7108 getAutoCreate : function(){
7114 cfg.cls += ' inverse';
7120 cfg.cls += " " + this.cls;
7138 * @class Roo.bootstrap.PaginationItem
7139 * @extends Roo.bootstrap.Component
7140 * Bootstrap PaginationItem class
7141 * @cfg {String} html text
7142 * @cfg {String} href the link
7143 * @cfg {Boolean} preventDefault (true | false) default true
7144 * @cfg {Boolean} active (true | false) default false
7145 * @cfg {Boolean} disabled default false
7149 * Create a new PaginationItem
7150 * @param {Object} config The config object
7154 Roo.bootstrap.PaginationItem = function(config){
7155 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7160 * The raw click event for the entire grid.
7161 * @param {Roo.EventObject} e
7167 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7171 preventDefault: true,
7176 getAutoCreate : function(){
7182 href : this.href ? this.href : '#',
7183 html : this.html ? this.html : ''
7193 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7197 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7203 initEvents: function() {
7205 this.el.on('click', this.onClick, this);
7208 onClick : function(e)
7210 Roo.log('PaginationItem on click ');
7211 if(this.preventDefault){
7219 this.fireEvent('click', this, e);
7235 * @class Roo.bootstrap.Slider
7236 * @extends Roo.bootstrap.Component
7237 * Bootstrap Slider class
7240 * Create a new Slider
7241 * @param {Object} config The config object
7244 Roo.bootstrap.Slider = function(config){
7245 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7248 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7250 getAutoCreate : function(){
7254 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7258 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7270 * Ext JS Library 1.1.1
7271 * Copyright(c) 2006-2007, Ext JS, LLC.
7273 * Originally Released Under LGPL - original licence link has changed is not relivant.
7276 * <script type="text/javascript">
7281 * @class Roo.grid.ColumnModel
7282 * @extends Roo.util.Observable
7283 * This is the default implementation of a ColumnModel used by the Grid. It defines
7284 * the columns in the grid.
7287 var colModel = new Roo.grid.ColumnModel([
7288 {header: "Ticker", width: 60, sortable: true, locked: true},
7289 {header: "Company Name", width: 150, sortable: true},
7290 {header: "Market Cap.", width: 100, sortable: true},
7291 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7292 {header: "Employees", width: 100, sortable: true, resizable: false}
7297 * The config options listed for this class are options which may appear in each
7298 * individual column definition.
7299 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7301 * @param {Object} config An Array of column config objects. See this class's
7302 * config objects for details.
7304 Roo.grid.ColumnModel = function(config){
7306 * The config passed into the constructor
7308 this.config = []; //config;
7311 // if no id, create one
7312 // if the column does not have a dataIndex mapping,
7313 // map it to the order it is in the config
7314 for(var i = 0, len = config.length; i < len; i++){
7315 this.addColumn(config[i]);
7320 * The width of columns which have no width specified (defaults to 100)
7323 this.defaultWidth = 100;
7326 * Default sortable of columns which have no sortable specified (defaults to false)
7329 this.defaultSortable = false;
7333 * @event widthchange
7334 * Fires when the width of a column changes.
7335 * @param {ColumnModel} this
7336 * @param {Number} columnIndex The column index
7337 * @param {Number} newWidth The new width
7339 "widthchange": true,
7341 * @event headerchange
7342 * Fires when the text of a header changes.
7343 * @param {ColumnModel} this
7344 * @param {Number} columnIndex The column index
7345 * @param {Number} newText The new header text
7347 "headerchange": true,
7349 * @event hiddenchange
7350 * Fires when a column is hidden or "unhidden".
7351 * @param {ColumnModel} this
7352 * @param {Number} columnIndex The column index
7353 * @param {Boolean} hidden true if hidden, false otherwise
7355 "hiddenchange": true,
7357 * @event columnmoved
7358 * Fires when a column is moved.
7359 * @param {ColumnModel} this
7360 * @param {Number} oldIndex
7361 * @param {Number} newIndex
7363 "columnmoved" : true,
7365 * @event columlockchange
7366 * Fires when a column's locked state is changed
7367 * @param {ColumnModel} this
7368 * @param {Number} colIndex
7369 * @param {Boolean} locked true if locked
7371 "columnlockchange" : true
7373 Roo.grid.ColumnModel.superclass.constructor.call(this);
7375 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7377 * @cfg {String} header The header text to display in the Grid view.
7380 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7381 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7382 * specified, the column's index is used as an index into the Record's data Array.
7385 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7386 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7389 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7390 * Defaults to the value of the {@link #defaultSortable} property.
7391 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7394 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7397 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7400 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7403 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7406 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7407 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7408 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7409 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7412 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7415 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7418 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7421 * @cfg {String} cursor (Optional)
7424 * @cfg {String} tooltip (Optional)
7427 * @cfg {Number} xs (Optional)
7430 * @cfg {Number} sm (Optional)
7433 * @cfg {Number} md (Optional)
7436 * @cfg {Number} lg (Optional)
7439 * Returns the id of the column at the specified index.
7440 * @param {Number} index The column index
7441 * @return {String} the id
7443 getColumnId : function(index){
7444 return this.config[index].id;
7448 * Returns the column for a specified id.
7449 * @param {String} id The column id
7450 * @return {Object} the column
7452 getColumnById : function(id){
7453 return this.lookup[id];
7458 * Returns the column Object for a specified dataIndex.
7459 * @param {String} dataIndex The column dataIndex
7460 * @return {Object|Boolean} the column or false if not found
7462 getColumnByDataIndex: function(dataIndex){
7463 var index = this.findColumnIndex(dataIndex);
7464 return index > -1 ? this.config[index] : false;
7468 * Returns the index for a specified column id.
7469 * @param {String} id The column id
7470 * @return {Number} the index, or -1 if not found
7472 getIndexById : function(id){
7473 for(var i = 0, len = this.config.length; i < len; i++){
7474 if(this.config[i].id == id){
7482 * Returns the index for a specified column dataIndex.
7483 * @param {String} dataIndex The column dataIndex
7484 * @return {Number} the index, or -1 if not found
7487 findColumnIndex : function(dataIndex){
7488 for(var i = 0, len = this.config.length; i < len; i++){
7489 if(this.config[i].dataIndex == dataIndex){
7497 moveColumn : function(oldIndex, newIndex){
7498 var c = this.config[oldIndex];
7499 this.config.splice(oldIndex, 1);
7500 this.config.splice(newIndex, 0, c);
7501 this.dataMap = null;
7502 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7505 isLocked : function(colIndex){
7506 return this.config[colIndex].locked === true;
7509 setLocked : function(colIndex, value, suppressEvent){
7510 if(this.isLocked(colIndex) == value){
7513 this.config[colIndex].locked = value;
7515 this.fireEvent("columnlockchange", this, colIndex, value);
7519 getTotalLockedWidth : function(){
7521 for(var i = 0; i < this.config.length; i++){
7522 if(this.isLocked(i) && !this.isHidden(i)){
7523 this.totalWidth += this.getColumnWidth(i);
7529 getLockedCount : function(){
7530 for(var i = 0, len = this.config.length; i < len; i++){
7531 if(!this.isLocked(i)){
7536 return this.config.length;
7540 * Returns the number of columns.
7543 getColumnCount : function(visibleOnly){
7544 if(visibleOnly === true){
7546 for(var i = 0, len = this.config.length; i < len; i++){
7547 if(!this.isHidden(i)){
7553 return this.config.length;
7557 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7558 * @param {Function} fn
7559 * @param {Object} scope (optional)
7560 * @return {Array} result
7562 getColumnsBy : function(fn, scope){
7564 for(var i = 0, len = this.config.length; i < len; i++){
7565 var c = this.config[i];
7566 if(fn.call(scope||this, c, i) === true){
7574 * Returns true if the specified column is sortable.
7575 * @param {Number} col The column index
7578 isSortable : function(col){
7579 if(typeof this.config[col].sortable == "undefined"){
7580 return this.defaultSortable;
7582 return this.config[col].sortable;
7586 * Returns the rendering (formatting) function defined for the column.
7587 * @param {Number} col The column index.
7588 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7590 getRenderer : function(col){
7591 if(!this.config[col].renderer){
7592 return Roo.grid.ColumnModel.defaultRenderer;
7594 return this.config[col].renderer;
7598 * Sets the rendering (formatting) function for a column.
7599 * @param {Number} col The column index
7600 * @param {Function} fn The function to use to process the cell's raw data
7601 * to return HTML markup for the grid view. The render function is called with
7602 * the following parameters:<ul>
7603 * <li>Data value.</li>
7604 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7605 * <li>css A CSS style string to apply to the table cell.</li>
7606 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7607 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7608 * <li>Row index</li>
7609 * <li>Column index</li>
7610 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7612 setRenderer : function(col, fn){
7613 this.config[col].renderer = fn;
7617 * Returns the width for the specified column.
7618 * @param {Number} col The column index
7621 getColumnWidth : function(col){
7622 return this.config[col].width * 1 || this.defaultWidth;
7626 * Sets the width for a column.
7627 * @param {Number} col The column index
7628 * @param {Number} width The new width
7630 setColumnWidth : function(col, width, suppressEvent){
7631 this.config[col].width = width;
7632 this.totalWidth = null;
7634 this.fireEvent("widthchange", this, col, width);
7639 * Returns the total width of all columns.
7640 * @param {Boolean} includeHidden True to include hidden column widths
7643 getTotalWidth : function(includeHidden){
7644 if(!this.totalWidth){
7645 this.totalWidth = 0;
7646 for(var i = 0, len = this.config.length; i < len; i++){
7647 if(includeHidden || !this.isHidden(i)){
7648 this.totalWidth += this.getColumnWidth(i);
7652 return this.totalWidth;
7656 * Returns the header for the specified column.
7657 * @param {Number} col The column index
7660 getColumnHeader : function(col){
7661 return this.config[col].header;
7665 * Sets the header for a column.
7666 * @param {Number} col The column index
7667 * @param {String} header The new header
7669 setColumnHeader : function(col, header){
7670 this.config[col].header = header;
7671 this.fireEvent("headerchange", this, col, header);
7675 * Returns the tooltip for the specified column.
7676 * @param {Number} col The column index
7679 getColumnTooltip : function(col){
7680 return this.config[col].tooltip;
7683 * Sets the tooltip for a column.
7684 * @param {Number} col The column index
7685 * @param {String} tooltip The new tooltip
7687 setColumnTooltip : function(col, tooltip){
7688 this.config[col].tooltip = tooltip;
7692 * Returns the dataIndex for the specified column.
7693 * @param {Number} col The column index
7696 getDataIndex : function(col){
7697 return this.config[col].dataIndex;
7701 * Sets the dataIndex for a column.
7702 * @param {Number} col The column index
7703 * @param {Number} dataIndex The new dataIndex
7705 setDataIndex : function(col, dataIndex){
7706 this.config[col].dataIndex = dataIndex;
7712 * Returns true if the cell is editable.
7713 * @param {Number} colIndex The column index
7714 * @param {Number} rowIndex The row index - this is nto actually used..?
7717 isCellEditable : function(colIndex, rowIndex){
7718 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7722 * Returns the editor defined for the cell/column.
7723 * return false or null to disable editing.
7724 * @param {Number} colIndex The column index
7725 * @param {Number} rowIndex The row index
7728 getCellEditor : function(colIndex, rowIndex){
7729 return this.config[colIndex].editor;
7733 * Sets if a column is editable.
7734 * @param {Number} col The column index
7735 * @param {Boolean} editable True if the column is editable
7737 setEditable : function(col, editable){
7738 this.config[col].editable = editable;
7743 * Returns true if the column is hidden.
7744 * @param {Number} colIndex The column index
7747 isHidden : function(colIndex){
7748 return this.config[colIndex].hidden;
7753 * Returns true if the column width cannot be changed
7755 isFixed : function(colIndex){
7756 return this.config[colIndex].fixed;
7760 * Returns true if the column can be resized
7763 isResizable : function(colIndex){
7764 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7767 * Sets if a column is hidden.
7768 * @param {Number} colIndex The column index
7769 * @param {Boolean} hidden True if the column is hidden
7771 setHidden : function(colIndex, hidden){
7772 this.config[colIndex].hidden = hidden;
7773 this.totalWidth = null;
7774 this.fireEvent("hiddenchange", this, colIndex, hidden);
7778 * Sets the editor for a column.
7779 * @param {Number} col The column index
7780 * @param {Object} editor The editor object
7782 setEditor : function(col, editor){
7783 this.config[col].editor = editor;
7786 * Add a column (experimental...) - defaults to adding to the end..
7787 * @param {Object} config
7789 addColumn : function(c)
7792 var i = this.config.length;
7795 if(typeof c.dataIndex == "undefined"){
7798 if(typeof c.renderer == "string"){
7799 c.renderer = Roo.util.Format[c.renderer];
7801 if(typeof c.id == "undefined"){
7804 if(c.editor && c.editor.xtype){
7805 c.editor = Roo.factory(c.editor, Roo.grid);
7807 if(c.editor && c.editor.isFormField){
7808 c.editor = new Roo.grid.GridEditor(c.editor);
7810 this.lookup[c.id] = c;
7815 Roo.grid.ColumnModel.defaultRenderer = function(value)
7817 if(typeof value == "object") {
7820 if(typeof value == "string" && value.length < 1){
7824 return String.format("{0}", value);
7827 // Alias for backwards compatibility
7828 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7831 * Ext JS Library 1.1.1
7832 * Copyright(c) 2006-2007, Ext JS, LLC.
7834 * Originally Released Under LGPL - original licence link has changed is not relivant.
7837 * <script type="text/javascript">
7841 * @class Roo.LoadMask
7842 * A simple utility class for generically masking elements while loading data. If the element being masked has
7843 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7844 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7845 * element's UpdateManager load indicator and will be destroyed after the initial load.
7847 * Create a new LoadMask
7848 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7849 * @param {Object} config The config object
7851 Roo.LoadMask = function(el, config){
7852 this.el = Roo.get(el);
7853 Roo.apply(this, config);
7855 this.store.on('beforeload', this.onBeforeLoad, this);
7856 this.store.on('load', this.onLoad, this);
7857 this.store.on('loadexception', this.onLoadException, this);
7858 this.removeMask = false;
7860 var um = this.el.getUpdateManager();
7861 um.showLoadIndicator = false; // disable the default indicator
7862 um.on('beforeupdate', this.onBeforeLoad, this);
7863 um.on('update', this.onLoad, this);
7864 um.on('failure', this.onLoad, this);
7865 this.removeMask = true;
7869 Roo.LoadMask.prototype = {
7871 * @cfg {Boolean} removeMask
7872 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7873 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7877 * The text to display in a centered loading message box (defaults to 'Loading...')
7881 * @cfg {String} msgCls
7882 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7884 msgCls : 'x-mask-loading',
7887 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7893 * Disables the mask to prevent it from being displayed
7895 disable : function(){
7896 this.disabled = true;
7900 * Enables the mask so that it can be displayed
7902 enable : function(){
7903 this.disabled = false;
7906 onLoadException : function()
7910 if (typeof(arguments[3]) != 'undefined') {
7911 Roo.MessageBox.alert("Error loading",arguments[3]);
7915 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7916 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7923 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7928 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7932 onBeforeLoad : function(){
7934 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7939 destroy : function(){
7941 this.store.un('beforeload', this.onBeforeLoad, this);
7942 this.store.un('load', this.onLoad, this);
7943 this.store.un('loadexception', this.onLoadException, this);
7945 var um = this.el.getUpdateManager();
7946 um.un('beforeupdate', this.onBeforeLoad, this);
7947 um.un('update', this.onLoad, this);
7948 um.un('failure', this.onLoad, this);
7959 * @class Roo.bootstrap.Table
7960 * @extends Roo.bootstrap.Component
7961 * Bootstrap Table class
7962 * @cfg {String} cls table class
7963 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7964 * @cfg {String} bgcolor Specifies the background color for a table
7965 * @cfg {Number} border Specifies whether the table cells should have borders or not
7966 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7967 * @cfg {Number} cellspacing Specifies the space between cells
7968 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7969 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7970 * @cfg {String} sortable Specifies that the table should be sortable
7971 * @cfg {String} summary Specifies a summary of the content of a table
7972 * @cfg {Number} width Specifies the width of a table
7973 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7975 * @cfg {boolean} striped Should the rows be alternative striped
7976 * @cfg {boolean} bordered Add borders to the table
7977 * @cfg {boolean} hover Add hover highlighting
7978 * @cfg {boolean} condensed Format condensed
7979 * @cfg {boolean} responsive Format condensed
7980 * @cfg {Boolean} loadMask (true|false) default false
7981 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7982 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7983 * @cfg {Boolean} rowSelection (true|false) default false
7984 * @cfg {Boolean} cellSelection (true|false) default false
7985 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7986 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7987 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7988 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7992 * Create a new Table
7993 * @param {Object} config The config object
7996 Roo.bootstrap.Table = function(config){
7997 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8002 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8003 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8004 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8005 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8007 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8009 this.sm.grid = this;
8010 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8011 this.sm = this.selModel;
8012 this.sm.xmodule = this.xmodule || false;
8015 if (this.cm && typeof(this.cm.config) == 'undefined') {
8016 this.colModel = new Roo.grid.ColumnModel(this.cm);
8017 this.cm = this.colModel;
8018 this.cm.xmodule = this.xmodule || false;
8021 this.store= Roo.factory(this.store, Roo.data);
8022 this.ds = this.store;
8023 this.ds.xmodule = this.xmodule || false;
8026 if (this.footer && this.store) {
8027 this.footer.dataSource = this.ds;
8028 this.footer = Roo.factory(this.footer);
8035 * Fires when a cell is clicked
8036 * @param {Roo.bootstrap.Table} this
8037 * @param {Roo.Element} el
8038 * @param {Number} rowIndex
8039 * @param {Number} columnIndex
8040 * @param {Roo.EventObject} e
8044 * @event celldblclick
8045 * Fires when a cell is double clicked
8046 * @param {Roo.bootstrap.Table} this
8047 * @param {Roo.Element} el
8048 * @param {Number} rowIndex
8049 * @param {Number} columnIndex
8050 * @param {Roo.EventObject} e
8052 "celldblclick" : true,
8055 * Fires when a row is clicked
8056 * @param {Roo.bootstrap.Table} this
8057 * @param {Roo.Element} el
8058 * @param {Number} rowIndex
8059 * @param {Roo.EventObject} e
8063 * @event rowdblclick
8064 * Fires when a row is double clicked
8065 * @param {Roo.bootstrap.Table} this
8066 * @param {Roo.Element} el
8067 * @param {Number} rowIndex
8068 * @param {Roo.EventObject} e
8070 "rowdblclick" : true,
8073 * Fires when a mouseover occur
8074 * @param {Roo.bootstrap.Table} this
8075 * @param {Roo.Element} el
8076 * @param {Number} rowIndex
8077 * @param {Number} columnIndex
8078 * @param {Roo.EventObject} e
8083 * Fires when a mouseout occur
8084 * @param {Roo.bootstrap.Table} this
8085 * @param {Roo.Element} el
8086 * @param {Number} rowIndex
8087 * @param {Number} columnIndex
8088 * @param {Roo.EventObject} e
8093 * Fires when a row is rendered, so you can change add a style to it.
8094 * @param {Roo.bootstrap.Table} this
8095 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8099 * @event rowsrendered
8100 * Fires when all the rows have been rendered
8101 * @param {Roo.bootstrap.Table} this
8103 'rowsrendered' : true,
8105 * @event contextmenu
8106 * The raw contextmenu event for the entire grid.
8107 * @param {Roo.EventObject} e
8109 "contextmenu" : true,
8111 * @event rowcontextmenu
8112 * Fires when a row is right clicked
8113 * @param {Roo.bootstrap.Table} this
8114 * @param {Number} rowIndex
8115 * @param {Roo.EventObject} e
8117 "rowcontextmenu" : true,
8119 * @event cellcontextmenu
8120 * Fires when a cell is right clicked
8121 * @param {Roo.bootstrap.Table} this
8122 * @param {Number} rowIndex
8123 * @param {Number} cellIndex
8124 * @param {Roo.EventObject} e
8126 "cellcontextmenu" : true,
8128 * @event headercontextmenu
8129 * Fires when a header is right clicked
8130 * @param {Roo.bootstrap.Table} this
8131 * @param {Number} columnIndex
8132 * @param {Roo.EventObject} e
8134 "headercontextmenu" : true
8138 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8164 rowSelection : false,
8165 cellSelection : false,
8168 // Roo.Element - the tbody
8170 // Roo.Element - thead element
8173 container: false, // used by gridpanel...
8179 auto_hide_footer : false,
8181 getAutoCreate : function()
8183 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8190 if (this.scrollBody) {
8191 cfg.cls += ' table-body-fixed';
8194 cfg.cls += ' table-striped';
8198 cfg.cls += ' table-hover';
8200 if (this.bordered) {
8201 cfg.cls += ' table-bordered';
8203 if (this.condensed) {
8204 cfg.cls += ' table-condensed';
8206 if (this.responsive) {
8207 cfg.cls += ' table-responsive';
8211 cfg.cls+= ' ' +this.cls;
8214 // this lot should be simplifed...
8227 ].forEach(function(k) {
8235 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8238 if(this.store || this.cm){
8239 if(this.headerShow){
8240 cfg.cn.push(this.renderHeader());
8243 cfg.cn.push(this.renderBody());
8245 if(this.footerShow){
8246 cfg.cn.push(this.renderFooter());
8248 // where does this come from?
8249 //cfg.cls+= ' TableGrid';
8252 return { cn : [ cfg ] };
8255 initEvents : function()
8257 if(!this.store || !this.cm){
8260 if (this.selModel) {
8261 this.selModel.initEvents();
8265 //Roo.log('initEvents with ds!!!!');
8267 this.mainBody = this.el.select('tbody', true).first();
8268 this.mainHead = this.el.select('thead', true).first();
8269 this.mainFoot = this.el.select('tfoot', true).first();
8274 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8275 e.on('click', this.sort, this);
8278 this.mainBody.on("click", this.onClick, this);
8279 this.mainBody.on("dblclick", this.onDblClick, this);
8281 // why is this done????? = it breaks dialogs??
8282 //this.parent().el.setStyle('position', 'relative');
8286 this.footer.parentId = this.id;
8287 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8290 this.el.select('tfoot tr td').first().addClass('hide');
8295 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8298 this.store.on('load', this.onLoad, this);
8299 this.store.on('beforeload', this.onBeforeLoad, this);
8300 this.store.on('update', this.onUpdate, this);
8301 this.store.on('add', this.onAdd, this);
8302 this.store.on("clear", this.clear, this);
8304 this.el.on("contextmenu", this.onContextMenu, this);
8306 this.mainBody.on('scroll', this.onBodyScroll, this);
8308 this.cm.on("headerchange", this.onHeaderChange, this);
8310 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8314 onContextMenu : function(e, t)
8316 this.processEvent("contextmenu", e);
8319 processEvent : function(name, e)
8321 if (name != 'touchstart' ) {
8322 this.fireEvent(name, e);
8325 var t = e.getTarget();
8327 var cell = Roo.get(t);
8333 if(cell.findParent('tfoot', false, true)){
8337 if(cell.findParent('thead', false, true)){
8339 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8340 cell = Roo.get(t).findParent('th', false, true);
8342 Roo.log("failed to find th in thead?");
8343 Roo.log(e.getTarget());
8348 var cellIndex = cell.dom.cellIndex;
8350 var ename = name == 'touchstart' ? 'click' : name;
8351 this.fireEvent("header" + ename, this, cellIndex, e);
8356 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8357 cell = Roo.get(t).findParent('td', false, true);
8359 Roo.log("failed to find th in tbody?");
8360 Roo.log(e.getTarget());
8365 var row = cell.findParent('tr', false, true);
8366 var cellIndex = cell.dom.cellIndex;
8367 var rowIndex = row.dom.rowIndex - 1;
8371 this.fireEvent("row" + name, this, rowIndex, e);
8375 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8381 onMouseover : function(e, el)
8383 var cell = Roo.get(el);
8389 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8390 cell = cell.findParent('td', false, true);
8393 var row = cell.findParent('tr', false, true);
8394 var cellIndex = cell.dom.cellIndex;
8395 var rowIndex = row.dom.rowIndex - 1; // start from 0
8397 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8401 onMouseout : function(e, el)
8403 var cell = Roo.get(el);
8409 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8410 cell = cell.findParent('td', false, true);
8413 var row = cell.findParent('tr', false, true);
8414 var cellIndex = cell.dom.cellIndex;
8415 var rowIndex = row.dom.rowIndex - 1; // start from 0
8417 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8421 onClick : function(e, el)
8423 var cell = Roo.get(el);
8425 if(!cell || (!this.cellSelection && !this.rowSelection)){
8429 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8430 cell = cell.findParent('td', false, true);
8433 if(!cell || typeof(cell) == 'undefined'){
8437 var row = cell.findParent('tr', false, true);
8439 if(!row || typeof(row) == 'undefined'){
8443 var cellIndex = cell.dom.cellIndex;
8444 var rowIndex = this.getRowIndex(row);
8446 // why??? - should these not be based on SelectionModel?
8447 if(this.cellSelection){
8448 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8451 if(this.rowSelection){
8452 this.fireEvent('rowclick', this, row, rowIndex, e);
8458 onDblClick : function(e,el)
8460 var cell = Roo.get(el);
8462 if(!cell || (!this.cellSelection && !this.rowSelection)){
8466 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8467 cell = cell.findParent('td', false, true);
8470 if(!cell || typeof(cell) == 'undefined'){
8474 var row = cell.findParent('tr', false, true);
8476 if(!row || typeof(row) == 'undefined'){
8480 var cellIndex = cell.dom.cellIndex;
8481 var rowIndex = this.getRowIndex(row);
8483 if(this.cellSelection){
8484 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8487 if(this.rowSelection){
8488 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8492 sort : function(e,el)
8494 var col = Roo.get(el);
8496 if(!col.hasClass('sortable')){
8500 var sort = col.attr('sort');
8503 if(col.select('i', true).first().hasClass('fa-arrow-up')){
8507 this.store.sortInfo = {field : sort, direction : dir};
8510 Roo.log("calling footer first");
8511 this.footer.onClick('first');
8514 this.store.load({ params : { start : 0 } });
8518 renderHeader : function()
8526 this.totalWidth = 0;
8528 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8530 var config = cm.config[i];
8534 cls : 'x-hcol-' + i,
8536 html: cm.getColumnHeader(i)
8541 if(typeof(config.sortable) != 'undefined' && config.sortable){
8543 c.html = '<i class="fa"></i>' + c.html;
8546 // could use BS4 hidden-..-down
8548 if(typeof(config.lgHeader) != 'undefined'){
8549 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8552 if(typeof(config.mdHeader) != 'undefined'){
8553 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8556 if(typeof(config.smHeader) != 'undefined'){
8557 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8560 if(typeof(config.xsHeader) != 'undefined'){
8561 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8568 if(typeof(config.tooltip) != 'undefined'){
8569 c.tooltip = config.tooltip;
8572 if(typeof(config.colspan) != 'undefined'){
8573 c.colspan = config.colspan;
8576 if(typeof(config.hidden) != 'undefined' && config.hidden){
8577 c.style += ' display:none;';
8580 if(typeof(config.dataIndex) != 'undefined'){
8581 c.sort = config.dataIndex;
8586 if(typeof(config.align) != 'undefined' && config.align.length){
8587 c.style += ' text-align:' + config.align + ';';
8590 if(typeof(config.width) != 'undefined'){
8591 c.style += ' width:' + config.width + 'px;';
8592 this.totalWidth += config.width;
8594 this.totalWidth += 100; // assume minimum of 100 per column?
8597 if(typeof(config.cls) != 'undefined'){
8598 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8601 ['xs','sm','md','lg'].map(function(size){
8603 if(typeof(config[size]) == 'undefined'){
8607 if (!config[size]) { // 0 = hidden
8608 // BS 4 '0' is treated as hide that column and below.
8609 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8613 c.cls += ' col-' + size + '-' + config[size] + (
8614 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8626 renderBody : function()
8636 colspan : this.cm.getColumnCount()
8646 renderFooter : function()
8656 colspan : this.cm.getColumnCount()
8670 // Roo.log('ds onload');
8675 var ds = this.store;
8677 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8678 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8679 if (_this.store.sortInfo) {
8681 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8682 e.select('i', true).addClass(['fa-arrow-up']);
8685 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8686 e.select('i', true).addClass(['fa-arrow-down']);
8691 var tbody = this.mainBody;
8693 if(ds.getCount() > 0){
8694 ds.data.each(function(d,rowIndex){
8695 var row = this.renderRow(cm, ds, rowIndex);
8697 tbody.createChild(row);
8701 if(row.cellObjects.length){
8702 Roo.each(row.cellObjects, function(r){
8703 _this.renderCellObject(r);
8710 var tfoot = this.el.select('tfoot', true).first();
8712 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8714 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8716 var total = this.ds.getTotalCount();
8718 if(this.footer.pageSize < total){
8719 this.mainFoot.show();
8723 Roo.each(this.el.select('tbody td', true).elements, function(e){
8724 e.on('mouseover', _this.onMouseover, _this);
8727 Roo.each(this.el.select('tbody td', true).elements, function(e){
8728 e.on('mouseout', _this.onMouseout, _this);
8730 this.fireEvent('rowsrendered', this);
8736 onUpdate : function(ds,record)
8738 this.refreshRow(record);
8742 onRemove : function(ds, record, index, isUpdate){
8743 if(isUpdate !== true){
8744 this.fireEvent("beforerowremoved", this, index, record);
8746 var bt = this.mainBody.dom;
8748 var rows = this.el.select('tbody > tr', true).elements;
8750 if(typeof(rows[index]) != 'undefined'){
8751 bt.removeChild(rows[index].dom);
8754 // if(bt.rows[index]){
8755 // bt.removeChild(bt.rows[index]);
8758 if(isUpdate !== true){
8759 //this.stripeRows(index);
8760 //this.syncRowHeights(index, index);
8762 this.fireEvent("rowremoved", this, index, record);
8766 onAdd : function(ds, records, rowIndex)
8768 //Roo.log('on Add called');
8769 // - note this does not handle multiple adding very well..
8770 var bt = this.mainBody.dom;
8771 for (var i =0 ; i < records.length;i++) {
8772 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8773 //Roo.log(records[i]);
8774 //Roo.log(this.store.getAt(rowIndex+i));
8775 this.insertRow(this.store, rowIndex + i, false);
8782 refreshRow : function(record){
8783 var ds = this.store, index;
8784 if(typeof record == 'number'){
8786 record = ds.getAt(index);
8788 index = ds.indexOf(record);
8790 return; // should not happen - but seems to
8793 this.insertRow(ds, index, true);
8795 this.onRemove(ds, record, index+1, true);
8797 //this.syncRowHeights(index, index);
8799 this.fireEvent("rowupdated", this, index, record);
8802 insertRow : function(dm, rowIndex, isUpdate){
8805 this.fireEvent("beforerowsinserted", this, rowIndex);
8807 //var s = this.getScrollState();
8808 var row = this.renderRow(this.cm, this.store, rowIndex);
8809 // insert before rowIndex..
8810 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8814 if(row.cellObjects.length){
8815 Roo.each(row.cellObjects, function(r){
8816 _this.renderCellObject(r);
8821 this.fireEvent("rowsinserted", this, rowIndex);
8822 //this.syncRowHeights(firstRow, lastRow);
8823 //this.stripeRows(firstRow);
8830 getRowDom : function(rowIndex)
8832 var rows = this.el.select('tbody > tr', true).elements;
8834 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8837 // returns the object tree for a tr..
8840 renderRow : function(cm, ds, rowIndex)
8842 var d = ds.getAt(rowIndex);
8846 cls : 'x-row-' + rowIndex,
8850 var cellObjects = [];
8852 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8853 var config = cm.config[i];
8855 var renderer = cm.getRenderer(i);
8859 if(typeof(renderer) !== 'undefined'){
8860 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8862 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8863 // and are rendered into the cells after the row is rendered - using the id for the element.
8865 if(typeof(value) === 'object'){
8875 rowIndex : rowIndex,
8880 this.fireEvent('rowclass', this, rowcfg);
8884 cls : rowcfg.rowClass + ' x-col-' + i,
8886 html: (typeof(value) === 'object') ? '' : value
8893 if(typeof(config.colspan) != 'undefined'){
8894 td.colspan = config.colspan;
8897 if(typeof(config.hidden) != 'undefined' && config.hidden){
8898 td.style += ' display:none;';
8901 if(typeof(config.align) != 'undefined' && config.align.length){
8902 td.style += ' text-align:' + config.align + ';';
8904 if(typeof(config.valign) != 'undefined' && config.valign.length){
8905 td.style += ' vertical-align:' + config.valign + ';';
8908 if(typeof(config.width) != 'undefined'){
8909 td.style += ' width:' + config.width + 'px;';
8912 if(typeof(config.cursor) != 'undefined'){
8913 td.style += ' cursor:' + config.cursor + ';';
8916 if(typeof(config.cls) != 'undefined'){
8917 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8920 ['xs','sm','md','lg'].map(function(size){
8922 if(typeof(config[size]) == 'undefined'){
8928 if (!config[size]) { // 0 = hidden
8929 // BS 4 '0' is treated as hide that column and below.
8930 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8934 td.cls += ' col-' + size + '-' + config[size] + (
8935 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8945 row.cellObjects = cellObjects;
8953 onBeforeLoad : function()
8962 this.el.select('tbody', true).first().dom.innerHTML = '';
8965 * Show or hide a row.
8966 * @param {Number} rowIndex to show or hide
8967 * @param {Boolean} state hide
8969 setRowVisibility : function(rowIndex, state)
8971 var bt = this.mainBody.dom;
8973 var rows = this.el.select('tbody > tr', true).elements;
8975 if(typeof(rows[rowIndex]) == 'undefined'){
8978 rows[rowIndex].dom.style.display = state ? '' : 'none';
8982 getSelectionModel : function(){
8984 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8986 return this.selModel;
8989 * Render the Roo.bootstrap object from renderder
8991 renderCellObject : function(r)
8995 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8997 var t = r.cfg.render(r.container);
9000 Roo.each(r.cfg.cn, function(c){
9002 container: t.getChildContainer(),
9005 _this.renderCellObject(child);
9010 getRowIndex : function(row)
9014 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9025 * Returns the grid's underlying element = used by panel.Grid
9026 * @return {Element} The element
9028 getGridEl : function(){
9032 * Forces a resize - used by panel.Grid
9033 * @return {Element} The element
9035 autoSize : function()
9037 //var ctr = Roo.get(this.container.dom.parentElement);
9038 var ctr = Roo.get(this.el.dom);
9040 var thd = this.getGridEl().select('thead',true).first();
9041 var tbd = this.getGridEl().select('tbody', true).first();
9042 var tfd = this.getGridEl().select('tfoot', true).first();
9044 var cw = ctr.getWidth();
9045 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9049 tbd.setWidth(ctr.getWidth());
9050 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9051 // this needs fixing for various usage - currently only hydra job advers I think..
9053 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9055 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9058 cw = Math.max(cw, this.totalWidth);
9059 this.getGridEl().select('tbody tr',true).setWidth(cw);
9061 // resize 'expandable coloumn?
9063 return; // we doe not have a view in this design..
9066 onBodyScroll: function()
9068 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9070 this.mainHead.setStyle({
9071 'position' : 'relative',
9072 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9078 var scrollHeight = this.mainBody.dom.scrollHeight;
9080 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9082 var height = this.mainBody.getHeight();
9084 if(scrollHeight - height == scrollTop) {
9086 var total = this.ds.getTotalCount();
9088 if(this.footer.cursor + this.footer.pageSize < total){
9090 this.footer.ds.load({
9092 start : this.footer.cursor + this.footer.pageSize,
9093 limit : this.footer.pageSize
9103 onHeaderChange : function()
9105 var header = this.renderHeader();
9106 var table = this.el.select('table', true).first();
9108 this.mainHead.remove();
9109 this.mainHead = table.createChild(header, this.mainBody, false);
9111 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9112 e.on('click', this.sort, this);
9118 onHiddenChange : function(colModel, colIndex, hidden)
9120 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9121 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9123 this.CSS.updateRule(thSelector, "display", "");
9124 this.CSS.updateRule(tdSelector, "display", "");
9127 this.CSS.updateRule(thSelector, "display", "none");
9128 this.CSS.updateRule(tdSelector, "display", "none");
9131 this.onHeaderChange();
9135 setColumnWidth: function(col_index, width)
9137 // width = "md-2 xs-2..."
9138 if(!this.colModel.config[col_index]) {
9142 var w = width.split(" ");
9144 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9146 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9149 for(var j = 0; j < w.length; j++) {
9155 var size_cls = w[j].split("-");
9157 if(!Number.isInteger(size_cls[1] * 1)) {
9161 if(!this.colModel.config[col_index][size_cls[0]]) {
9165 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9169 h_row[0].classList.replace(
9170 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9171 "col-"+size_cls[0]+"-"+size_cls[1]
9174 for(var i = 0; i < rows.length; i++) {
9176 var size_cls = w[j].split("-");
9178 if(!Number.isInteger(size_cls[1] * 1)) {
9182 if(!this.colModel.config[col_index][size_cls[0]]) {
9186 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9190 rows[i].classList.replace(
9191 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9192 "col-"+size_cls[0]+"-"+size_cls[1]
9196 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9211 * @class Roo.bootstrap.TableCell
9212 * @extends Roo.bootstrap.Component
9213 * Bootstrap TableCell class
9214 * @cfg {String} html cell contain text
9215 * @cfg {String} cls cell class
9216 * @cfg {String} tag cell tag (td|th) default td
9217 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9218 * @cfg {String} align Aligns the content in a cell
9219 * @cfg {String} axis Categorizes cells
9220 * @cfg {String} bgcolor Specifies the background color of a cell
9221 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9222 * @cfg {Number} colspan Specifies the number of columns a cell should span
9223 * @cfg {String} headers Specifies one or more header cells a cell is related to
9224 * @cfg {Number} height Sets the height of a cell
9225 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9226 * @cfg {Number} rowspan Sets the number of rows a cell should span
9227 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9228 * @cfg {String} valign Vertical aligns the content in a cell
9229 * @cfg {Number} width Specifies the width of a cell
9232 * Create a new TableCell
9233 * @param {Object} config The config object
9236 Roo.bootstrap.TableCell = function(config){
9237 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9240 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9260 getAutoCreate : function(){
9261 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9281 cfg.align=this.align
9287 cfg.bgcolor=this.bgcolor
9290 cfg.charoff=this.charoff
9293 cfg.colspan=this.colspan
9296 cfg.headers=this.headers
9299 cfg.height=this.height
9302 cfg.nowrap=this.nowrap
9305 cfg.rowspan=this.rowspan
9308 cfg.scope=this.scope
9311 cfg.valign=this.valign
9314 cfg.width=this.width
9333 * @class Roo.bootstrap.TableRow
9334 * @extends Roo.bootstrap.Component
9335 * Bootstrap TableRow class
9336 * @cfg {String} cls row class
9337 * @cfg {String} align Aligns the content in a table row
9338 * @cfg {String} bgcolor Specifies a background color for a table row
9339 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9340 * @cfg {String} valign Vertical aligns the content in a table row
9343 * Create a new TableRow
9344 * @param {Object} config The config object
9347 Roo.bootstrap.TableRow = function(config){
9348 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9351 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9359 getAutoCreate : function(){
9360 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9370 cfg.align = this.align;
9373 cfg.bgcolor = this.bgcolor;
9376 cfg.charoff = this.charoff;
9379 cfg.valign = this.valign;
9397 * @class Roo.bootstrap.TableBody
9398 * @extends Roo.bootstrap.Component
9399 * Bootstrap TableBody class
9400 * @cfg {String} cls element class
9401 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9402 * @cfg {String} align Aligns the content inside the element
9403 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9404 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9407 * Create a new TableBody
9408 * @param {Object} config The config object
9411 Roo.bootstrap.TableBody = function(config){
9412 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9415 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9423 getAutoCreate : function(){
9424 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9438 cfg.align = this.align;
9441 cfg.charoff = this.charoff;
9444 cfg.valign = this.valign;
9451 // initEvents : function()
9458 // this.store = Roo.factory(this.store, Roo.data);
9459 // this.store.on('load', this.onLoad, this);
9461 // this.store.load();
9465 // onLoad: function ()
9467 // this.fireEvent('load', this);
9477 * Ext JS Library 1.1.1
9478 * Copyright(c) 2006-2007, Ext JS, LLC.
9480 * Originally Released Under LGPL - original licence link has changed is not relivant.
9483 * <script type="text/javascript">
9486 // as we use this in bootstrap.
9487 Roo.namespace('Roo.form');
9489 * @class Roo.form.Action
9490 * Internal Class used to handle form actions
9492 * @param {Roo.form.BasicForm} el The form element or its id
9493 * @param {Object} config Configuration options
9498 // define the action interface
9499 Roo.form.Action = function(form, options){
9501 this.options = options || {};
9504 * Client Validation Failed
9507 Roo.form.Action.CLIENT_INVALID = 'client';
9509 * Server Validation Failed
9512 Roo.form.Action.SERVER_INVALID = 'server';
9514 * Connect to Server Failed
9517 Roo.form.Action.CONNECT_FAILURE = 'connect';
9519 * Reading Data from Server Failed
9522 Roo.form.Action.LOAD_FAILURE = 'load';
9524 Roo.form.Action.prototype = {
9526 failureType : undefined,
9527 response : undefined,
9531 run : function(options){
9536 success : function(response){
9541 handleResponse : function(response){
9545 // default connection failure
9546 failure : function(response){
9548 this.response = response;
9549 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9550 this.form.afterAction(this, false);
9553 processResponse : function(response){
9554 this.response = response;
9555 if(!response.responseText){
9558 this.result = this.handleResponse(response);
9562 // utility functions used internally
9563 getUrl : function(appendParams){
9564 var url = this.options.url || this.form.url || this.form.el.dom.action;
9566 var p = this.getParams();
9568 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9574 getMethod : function(){
9575 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9578 getParams : function(){
9579 var bp = this.form.baseParams;
9580 var p = this.options.params;
9582 if(typeof p == "object"){
9583 p = Roo.urlEncode(Roo.applyIf(p, bp));
9584 }else if(typeof p == 'string' && bp){
9585 p += '&' + Roo.urlEncode(bp);
9588 p = Roo.urlEncode(bp);
9593 createCallback : function(){
9595 success: this.success,
9596 failure: this.failure,
9598 timeout: (this.form.timeout*1000),
9599 upload: this.form.fileUpload ? this.success : undefined
9604 Roo.form.Action.Submit = function(form, options){
9605 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9608 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9611 haveProgress : false,
9612 uploadComplete : false,
9614 // uploadProgress indicator.
9615 uploadProgress : function()
9617 if (!this.form.progressUrl) {
9621 if (!this.haveProgress) {
9622 Roo.MessageBox.progress("Uploading", "Uploading");
9624 if (this.uploadComplete) {
9625 Roo.MessageBox.hide();
9629 this.haveProgress = true;
9631 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9633 var c = new Roo.data.Connection();
9635 url : this.form.progressUrl,
9640 success : function(req){
9641 //console.log(data);
9645 rdata = Roo.decode(req.responseText)
9647 Roo.log("Invalid data from server..");
9651 if (!rdata || !rdata.success) {
9653 Roo.MessageBox.alert(Roo.encode(rdata));
9656 var data = rdata.data;
9658 if (this.uploadComplete) {
9659 Roo.MessageBox.hide();
9664 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9665 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9668 this.uploadProgress.defer(2000,this);
9671 failure: function(data) {
9672 Roo.log('progress url failed ');
9683 // run get Values on the form, so it syncs any secondary forms.
9684 this.form.getValues();
9686 var o = this.options;
9687 var method = this.getMethod();
9688 var isPost = method == 'POST';
9689 if(o.clientValidation === false || this.form.isValid()){
9691 if (this.form.progressUrl) {
9692 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9693 (new Date() * 1) + '' + Math.random());
9698 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9699 form:this.form.el.dom,
9700 url:this.getUrl(!isPost),
9702 params:isPost ? this.getParams() : null,
9703 isUpload: this.form.fileUpload,
9704 formData : this.form.formData
9707 this.uploadProgress();
9709 }else if (o.clientValidation !== false){ // client validation failed
9710 this.failureType = Roo.form.Action.CLIENT_INVALID;
9711 this.form.afterAction(this, false);
9715 success : function(response)
9717 this.uploadComplete= true;
9718 if (this.haveProgress) {
9719 Roo.MessageBox.hide();
9723 var result = this.processResponse(response);
9724 if(result === true || result.success){
9725 this.form.afterAction(this, true);
9729 this.form.markInvalid(result.errors);
9730 this.failureType = Roo.form.Action.SERVER_INVALID;
9732 this.form.afterAction(this, false);
9734 failure : function(response)
9736 this.uploadComplete= true;
9737 if (this.haveProgress) {
9738 Roo.MessageBox.hide();
9741 this.response = response;
9742 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9743 this.form.afterAction(this, false);
9746 handleResponse : function(response){
9747 if(this.form.errorReader){
9748 var rs = this.form.errorReader.read(response);
9751 for(var i = 0, len = rs.records.length; i < len; i++) {
9752 var r = rs.records[i];
9756 if(errors.length < 1){
9760 success : rs.success,
9766 ret = Roo.decode(response.responseText);
9770 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9780 Roo.form.Action.Load = function(form, options){
9781 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9782 this.reader = this.form.reader;
9785 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9790 Roo.Ajax.request(Roo.apply(
9791 this.createCallback(), {
9792 method:this.getMethod(),
9793 url:this.getUrl(false),
9794 params:this.getParams()
9798 success : function(response){
9800 var result = this.processResponse(response);
9801 if(result === true || !result.success || !result.data){
9802 this.failureType = Roo.form.Action.LOAD_FAILURE;
9803 this.form.afterAction(this, false);
9806 this.form.clearInvalid();
9807 this.form.setValues(result.data);
9808 this.form.afterAction(this, true);
9811 handleResponse : function(response){
9812 if(this.form.reader){
9813 var rs = this.form.reader.read(response);
9814 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9816 success : rs.success,
9820 return Roo.decode(response.responseText);
9824 Roo.form.Action.ACTION_TYPES = {
9825 'load' : Roo.form.Action.Load,
9826 'submit' : Roo.form.Action.Submit
9835 * @class Roo.bootstrap.Form
9836 * @extends Roo.bootstrap.Component
9837 * Bootstrap Form class
9838 * @cfg {String} method GET | POST (default POST)
9839 * @cfg {String} labelAlign top | left (default top)
9840 * @cfg {String} align left | right - for navbars
9841 * @cfg {Boolean} loadMask load mask when submit (default true)
9846 * @param {Object} config The config object
9850 Roo.bootstrap.Form = function(config){
9852 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9854 Roo.bootstrap.Form.popover.apply();
9858 * @event clientvalidation
9859 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9860 * @param {Form} this
9861 * @param {Boolean} valid true if the form has passed client-side validation
9863 clientvalidation: true,
9865 * @event beforeaction
9866 * Fires before any action is performed. Return false to cancel the action.
9867 * @param {Form} this
9868 * @param {Action} action The action to be performed
9872 * @event actionfailed
9873 * Fires when an action fails.
9874 * @param {Form} this
9875 * @param {Action} action The action that failed
9877 actionfailed : true,
9879 * @event actioncomplete
9880 * Fires when an action is completed.
9881 * @param {Form} this
9882 * @param {Action} action The action that completed
9884 actioncomplete : true
9888 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9891 * @cfg {String} method
9892 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9897 * The URL to use for form actions if one isn't supplied in the action options.
9900 * @cfg {Boolean} fileUpload
9901 * Set to true if this form is a file upload.
9905 * @cfg {Object} baseParams
9906 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9910 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9914 * @cfg {Sting} align (left|right) for navbar forms
9919 activeAction : null,
9922 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9923 * element by passing it or its id or mask the form itself by passing in true.
9926 waitMsgTarget : false,
9931 * @cfg {Boolean} errorMask (true|false) default false
9936 * @cfg {Number} maskOffset Default 100
9941 * @cfg {Boolean} maskBody
9945 getAutoCreate : function(){
9949 method : this.method || 'POST',
9950 id : this.id || Roo.id(),
9953 if (this.parent().xtype.match(/^Nav/)) {
9954 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9958 if (this.labelAlign == 'left' ) {
9959 cfg.cls += ' form-horizontal';
9965 initEvents : function()
9967 this.el.on('submit', this.onSubmit, this);
9968 // this was added as random key presses on the form where triggering form submit.
9969 this.el.on('keypress', function(e) {
9970 if (e.getCharCode() != 13) {
9973 // we might need to allow it for textareas.. and some other items.
9974 // check e.getTarget().
9976 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9980 Roo.log("keypress blocked");
9988 onSubmit : function(e){
9993 * Returns true if client-side validation on the form is successful.
9996 isValid : function(){
9997 var items = this.getItems();
10001 items.each(function(f){
10007 Roo.log('invalid field: ' + f.name);
10011 if(!target && f.el.isVisible(true)){
10017 if(this.errorMask && !valid){
10018 Roo.bootstrap.Form.popover.mask(this, target);
10025 * Returns true if any fields in this form have changed since their original load.
10028 isDirty : function(){
10030 var items = this.getItems();
10031 items.each(function(f){
10041 * Performs a predefined action (submit or load) or custom actions you define on this form.
10042 * @param {String} actionName The name of the action type
10043 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10044 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10045 * accept other config options):
10047 Property Type Description
10048 ---------------- --------------- ----------------------------------------------------------------------------------
10049 url String The url for the action (defaults to the form's url)
10050 method String The form method to use (defaults to the form's method, or POST if not defined)
10051 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10052 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10053 validate the form on the client (defaults to false)
10055 * @return {BasicForm} this
10057 doAction : function(action, options){
10058 if(typeof action == 'string'){
10059 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10061 if(this.fireEvent('beforeaction', this, action) !== false){
10062 this.beforeAction(action);
10063 action.run.defer(100, action);
10069 beforeAction : function(action){
10070 var o = action.options;
10075 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10077 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10080 // not really supported yet.. ??
10082 //if(this.waitMsgTarget === true){
10083 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10084 //}else if(this.waitMsgTarget){
10085 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10086 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10088 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10094 afterAction : function(action, success){
10095 this.activeAction = null;
10096 var o = action.options;
10101 Roo.get(document.body).unmask();
10107 //if(this.waitMsgTarget === true){
10108 // this.el.unmask();
10109 //}else if(this.waitMsgTarget){
10110 // this.waitMsgTarget.unmask();
10112 // Roo.MessageBox.updateProgress(1);
10113 // Roo.MessageBox.hide();
10120 Roo.callback(o.success, o.scope, [this, action]);
10121 this.fireEvent('actioncomplete', this, action);
10125 // failure condition..
10126 // we have a scenario where updates need confirming.
10127 // eg. if a locking scenario exists..
10128 // we look for { errors : { needs_confirm : true }} in the response.
10130 (typeof(action.result) != 'undefined') &&
10131 (typeof(action.result.errors) != 'undefined') &&
10132 (typeof(action.result.errors.needs_confirm) != 'undefined')
10135 Roo.log("not supported yet");
10138 Roo.MessageBox.confirm(
10139 "Change requires confirmation",
10140 action.result.errorMsg,
10145 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10155 Roo.callback(o.failure, o.scope, [this, action]);
10156 // show an error message if no failed handler is set..
10157 if (!this.hasListener('actionfailed')) {
10158 Roo.log("need to add dialog support");
10160 Roo.MessageBox.alert("Error",
10161 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10162 action.result.errorMsg :
10163 "Saving Failed, please check your entries or try again"
10168 this.fireEvent('actionfailed', this, action);
10173 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10174 * @param {String} id The value to search for
10177 findField : function(id){
10178 var items = this.getItems();
10179 var field = items.get(id);
10181 items.each(function(f){
10182 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10189 return field || null;
10192 * Mark fields in this form invalid in bulk.
10193 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10194 * @return {BasicForm} this
10196 markInvalid : function(errors){
10197 if(errors instanceof Array){
10198 for(var i = 0, len = errors.length; i < len; i++){
10199 var fieldError = errors[i];
10200 var f = this.findField(fieldError.id);
10202 f.markInvalid(fieldError.msg);
10208 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10209 field.markInvalid(errors[id]);
10213 //Roo.each(this.childForms || [], function (f) {
10214 // f.markInvalid(errors);
10221 * Set values for fields in this form in bulk.
10222 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10223 * @return {BasicForm} this
10225 setValues : function(values){
10226 if(values instanceof Array){ // array of objects
10227 for(var i = 0, len = values.length; i < len; i++){
10229 var f = this.findField(v.id);
10231 f.setValue(v.value);
10232 if(this.trackResetOnLoad){
10233 f.originalValue = f.getValue();
10237 }else{ // object hash
10240 if(typeof values[id] != 'function' && (field = this.findField(id))){
10242 if (field.setFromData &&
10243 field.valueField &&
10244 field.displayField &&
10245 // combos' with local stores can
10246 // be queried via setValue()
10247 // to set their value..
10248 (field.store && !field.store.isLocal)
10252 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10253 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10254 field.setFromData(sd);
10256 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10258 field.setFromData(values);
10261 field.setValue(values[id]);
10265 if(this.trackResetOnLoad){
10266 field.originalValue = field.getValue();
10272 //Roo.each(this.childForms || [], function (f) {
10273 // f.setValues(values);
10280 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10281 * they are returned as an array.
10282 * @param {Boolean} asString
10285 getValues : function(asString){
10286 //if (this.childForms) {
10287 // copy values from the child forms
10288 // Roo.each(this.childForms, function (f) {
10289 // this.setValues(f.getValues());
10295 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10296 if(asString === true){
10299 return Roo.urlDecode(fs);
10303 * Returns the fields in this form as an object with key/value pairs.
10304 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10307 getFieldValues : function(with_hidden)
10309 var items = this.getItems();
10311 items.each(function(f){
10313 if (!f.getName()) {
10317 var v = f.getValue();
10319 if (f.inputType =='radio') {
10320 if (typeof(ret[f.getName()]) == 'undefined') {
10321 ret[f.getName()] = ''; // empty..
10324 if (!f.el.dom.checked) {
10328 v = f.el.dom.value;
10332 if(f.xtype == 'MoneyField'){
10333 ret[f.currencyName] = f.getCurrency();
10336 // not sure if this supported any more..
10337 if ((typeof(v) == 'object') && f.getRawValue) {
10338 v = f.getRawValue() ; // dates..
10340 // combo boxes where name != hiddenName...
10341 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10342 ret[f.name] = f.getRawValue();
10344 ret[f.getName()] = v;
10351 * Clears all invalid messages in this form.
10352 * @return {BasicForm} this
10354 clearInvalid : function(){
10355 var items = this.getItems();
10357 items.each(function(f){
10365 * Resets this form.
10366 * @return {BasicForm} this
10368 reset : function(){
10369 var items = this.getItems();
10370 items.each(function(f){
10374 Roo.each(this.childForms || [], function (f) {
10382 getItems : function()
10384 var r=new Roo.util.MixedCollection(false, function(o){
10385 return o.id || (o.id = Roo.id());
10387 var iter = function(el) {
10394 Roo.each(el.items,function(e) {
10403 hideFields : function(items)
10405 Roo.each(items, function(i){
10407 var f = this.findField(i);
10418 showFields : function(items)
10420 Roo.each(items, function(i){
10422 var f = this.findField(i);
10435 Roo.apply(Roo.bootstrap.Form, {
10451 intervalID : false,
10457 if(this.isApplied){
10462 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10463 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10464 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10465 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10468 this.maskEl.top.enableDisplayMode("block");
10469 this.maskEl.left.enableDisplayMode("block");
10470 this.maskEl.bottom.enableDisplayMode("block");
10471 this.maskEl.right.enableDisplayMode("block");
10473 this.toolTip = new Roo.bootstrap.Tooltip({
10474 cls : 'roo-form-error-popover',
10476 'left' : ['r-l', [-2,0], 'right'],
10477 'right' : ['l-r', [2,0], 'left'],
10478 'bottom' : ['tl-bl', [0,2], 'top'],
10479 'top' : [ 'bl-tl', [0,-2], 'bottom']
10483 this.toolTip.render(Roo.get(document.body));
10485 this.toolTip.el.enableDisplayMode("block");
10487 Roo.get(document.body).on('click', function(){
10491 Roo.get(document.body).on('touchstart', function(){
10495 this.isApplied = true
10498 mask : function(form, target)
10502 this.target = target;
10504 if(!this.form.errorMask || !target.el){
10508 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10510 Roo.log(scrollable);
10512 var ot = this.target.el.calcOffsetsTo(scrollable);
10514 var scrollTo = ot[1] - this.form.maskOffset;
10516 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10518 scrollable.scrollTo('top', scrollTo);
10520 var box = this.target.el.getBox();
10522 var zIndex = Roo.bootstrap.Modal.zIndex++;
10525 this.maskEl.top.setStyle('position', 'absolute');
10526 this.maskEl.top.setStyle('z-index', zIndex);
10527 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10528 this.maskEl.top.setLeft(0);
10529 this.maskEl.top.setTop(0);
10530 this.maskEl.top.show();
10532 this.maskEl.left.setStyle('position', 'absolute');
10533 this.maskEl.left.setStyle('z-index', zIndex);
10534 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10535 this.maskEl.left.setLeft(0);
10536 this.maskEl.left.setTop(box.y - this.padding);
10537 this.maskEl.left.show();
10539 this.maskEl.bottom.setStyle('position', 'absolute');
10540 this.maskEl.bottom.setStyle('z-index', zIndex);
10541 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10542 this.maskEl.bottom.setLeft(0);
10543 this.maskEl.bottom.setTop(box.bottom + this.padding);
10544 this.maskEl.bottom.show();
10546 this.maskEl.right.setStyle('position', 'absolute');
10547 this.maskEl.right.setStyle('z-index', zIndex);
10548 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10549 this.maskEl.right.setLeft(box.right + this.padding);
10550 this.maskEl.right.setTop(box.y - this.padding);
10551 this.maskEl.right.show();
10553 this.toolTip.bindEl = this.target.el;
10555 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10557 var tip = this.target.blankText;
10559 if(this.target.getValue() !== '' ) {
10561 if (this.target.invalidText.length) {
10562 tip = this.target.invalidText;
10563 } else if (this.target.regexText.length){
10564 tip = this.target.regexText;
10568 this.toolTip.show(tip);
10570 this.intervalID = window.setInterval(function() {
10571 Roo.bootstrap.Form.popover.unmask();
10574 window.onwheel = function(){ return false;};
10576 (function(){ this.isMasked = true; }).defer(500, this);
10580 unmask : function()
10582 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10586 this.maskEl.top.setStyle('position', 'absolute');
10587 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10588 this.maskEl.top.hide();
10590 this.maskEl.left.setStyle('position', 'absolute');
10591 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10592 this.maskEl.left.hide();
10594 this.maskEl.bottom.setStyle('position', 'absolute');
10595 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10596 this.maskEl.bottom.hide();
10598 this.maskEl.right.setStyle('position', 'absolute');
10599 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10600 this.maskEl.right.hide();
10602 this.toolTip.hide();
10604 this.toolTip.el.hide();
10606 window.onwheel = function(){ return true;};
10608 if(this.intervalID){
10609 window.clearInterval(this.intervalID);
10610 this.intervalID = false;
10613 this.isMasked = false;
10623 * Ext JS Library 1.1.1
10624 * Copyright(c) 2006-2007, Ext JS, LLC.
10626 * Originally Released Under LGPL - original licence link has changed is not relivant.
10629 * <script type="text/javascript">
10632 * @class Roo.form.VTypes
10633 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10636 Roo.form.VTypes = function(){
10637 // closure these in so they are only created once.
10638 var alpha = /^[a-zA-Z_]+$/;
10639 var alphanum = /^[a-zA-Z0-9_]+$/;
10640 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10641 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10643 // All these messages and functions are configurable
10646 * The function used to validate email addresses
10647 * @param {String} value The email address
10649 'email' : function(v){
10650 return email.test(v);
10653 * The error text to display when the email validation function returns false
10656 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10658 * The keystroke filter mask to be applied on email input
10661 'emailMask' : /[a-z0-9_\.\-@]/i,
10664 * The function used to validate URLs
10665 * @param {String} value The URL
10667 'url' : function(v){
10668 return url.test(v);
10671 * The error text to display when the url validation function returns false
10674 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10677 * The function used to validate alpha values
10678 * @param {String} value The value
10680 'alpha' : function(v){
10681 return alpha.test(v);
10684 * The error text to display when the alpha validation function returns false
10687 'alphaText' : 'This field should only contain letters and _',
10689 * The keystroke filter mask to be applied on alpha input
10692 'alphaMask' : /[a-z_]/i,
10695 * The function used to validate alphanumeric values
10696 * @param {String} value The value
10698 'alphanum' : function(v){
10699 return alphanum.test(v);
10702 * The error text to display when the alphanumeric validation function returns false
10705 'alphanumText' : 'This field should only contain letters, numbers and _',
10707 * The keystroke filter mask to be applied on alphanumeric input
10710 'alphanumMask' : /[a-z0-9_]/i
10720 * @class Roo.bootstrap.Input
10721 * @extends Roo.bootstrap.Component
10722 * Bootstrap Input class
10723 * @cfg {Boolean} disabled is it disabled
10724 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10725 * @cfg {String} name name of the input
10726 * @cfg {string} fieldLabel - the label associated
10727 * @cfg {string} placeholder - placeholder to put in text.
10728 * @cfg {string} before - input group add on before
10729 * @cfg {string} after - input group add on after
10730 * @cfg {string} size - (lg|sm) or leave empty..
10731 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10732 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10733 * @cfg {Number} md colspan out of 12 for computer-sized screens
10734 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10735 * @cfg {string} value default value of the input
10736 * @cfg {Number} labelWidth set the width of label
10737 * @cfg {Number} labellg set the width of label (1-12)
10738 * @cfg {Number} labelmd set the width of label (1-12)
10739 * @cfg {Number} labelsm set the width of label (1-12)
10740 * @cfg {Number} labelxs set the width of label (1-12)
10741 * @cfg {String} labelAlign (top|left)
10742 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10743 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10744 * @cfg {String} indicatorpos (left|right) default left
10745 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10746 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10747 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10749 * @cfg {String} align (left|center|right) Default left
10750 * @cfg {Boolean} forceFeedback (true|false) Default false
10753 * Create a new Input
10754 * @param {Object} config The config object
10757 Roo.bootstrap.Input = function(config){
10759 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10764 * Fires when this field receives input focus.
10765 * @param {Roo.form.Field} this
10770 * Fires when this field loses input focus.
10771 * @param {Roo.form.Field} this
10775 * @event specialkey
10776 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10777 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10778 * @param {Roo.form.Field} this
10779 * @param {Roo.EventObject} e The event object
10784 * Fires just before the field blurs if the field value has changed.
10785 * @param {Roo.form.Field} this
10786 * @param {Mixed} newValue The new value
10787 * @param {Mixed} oldValue The original value
10792 * Fires after the field has been marked as invalid.
10793 * @param {Roo.form.Field} this
10794 * @param {String} msg The validation message
10799 * Fires after the field has been validated with no errors.
10800 * @param {Roo.form.Field} this
10805 * Fires after the key up
10806 * @param {Roo.form.Field} this
10807 * @param {Roo.EventObject} e The event Object
10812 * Fires after the user pastes into input
10813 * @param {Roo.form.Field} this
10814 * @param {Roo.EventObject} e The event Object
10820 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10822 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10823 automatic validation (defaults to "keyup").
10825 validationEvent : "keyup",
10827 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10829 validateOnBlur : true,
10831 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10833 validationDelay : 250,
10835 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10837 focusClass : "x-form-focus", // not needed???
10841 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10843 invalidClass : "has-warning",
10846 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10848 validClass : "has-success",
10851 * @cfg {Boolean} hasFeedback (true|false) default true
10853 hasFeedback : true,
10856 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10858 invalidFeedbackClass : "glyphicon-warning-sign",
10861 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10863 validFeedbackClass : "glyphicon-ok",
10866 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10868 selectOnFocus : false,
10871 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10875 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10880 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10882 disableKeyFilter : false,
10885 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10889 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10893 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10895 blankText : "Please complete this mandatory field",
10898 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10902 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10904 maxLength : Number.MAX_VALUE,
10906 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10908 minLengthText : "The minimum length for this field is {0}",
10910 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10912 maxLengthText : "The maximum length for this field is {0}",
10916 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10917 * If available, this function will be called only after the basic validators all return true, and will be passed the
10918 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10922 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10923 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10924 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10928 * @cfg {String} regexText -- Depricated - use Invalid Text
10933 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10939 autocomplete: false,
10943 inputType : 'text',
10946 placeholder: false,
10951 preventMark: false,
10952 isFormField : true,
10955 labelAlign : false,
10958 formatedValue : false,
10959 forceFeedback : false,
10961 indicatorpos : 'left',
10971 parentLabelAlign : function()
10974 while (parent.parent()) {
10975 parent = parent.parent();
10976 if (typeof(parent.labelAlign) !='undefined') {
10977 return parent.labelAlign;
10984 getAutoCreate : function()
10986 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10992 if(this.inputType != 'hidden'){
10993 cfg.cls = 'form-group' //input-group
10999 type : this.inputType,
11000 value : this.value,
11001 cls : 'form-control',
11002 placeholder : this.placeholder || '',
11003 autocomplete : this.autocomplete || 'new-password'
11005 if (this.inputType == 'file') {
11006 input.style = 'overflow:hidden'; // why not in CSS?
11009 if(this.capture.length){
11010 input.capture = this.capture;
11013 if(this.accept.length){
11014 input.accept = this.accept + "/*";
11018 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11021 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11022 input.maxLength = this.maxLength;
11025 if (this.disabled) {
11026 input.disabled=true;
11029 if (this.readOnly) {
11030 input.readonly=true;
11034 input.name = this.name;
11038 input.cls += ' input-' + this.size;
11042 ['xs','sm','md','lg'].map(function(size){
11043 if (settings[size]) {
11044 cfg.cls += ' col-' + size + '-' + settings[size];
11048 var inputblock = input;
11052 cls: 'glyphicon form-control-feedback'
11055 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11058 cls : 'has-feedback',
11066 if (this.before || this.after) {
11069 cls : 'input-group',
11073 if (this.before && typeof(this.before) == 'string') {
11075 inputblock.cn.push({
11077 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11081 if (this.before && typeof(this.before) == 'object') {
11082 this.before = Roo.factory(this.before);
11084 inputblock.cn.push({
11086 cls : 'roo-input-before input-group-prepend input-group-' +
11087 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11091 inputblock.cn.push(input);
11093 if (this.after && typeof(this.after) == 'string') {
11094 inputblock.cn.push({
11096 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11100 if (this.after && typeof(this.after) == 'object') {
11101 this.after = Roo.factory(this.after);
11103 inputblock.cn.push({
11105 cls : 'roo-input-after input-group-append input-group-' +
11106 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11110 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11111 inputblock.cls += ' has-feedback';
11112 inputblock.cn.push(feedback);
11117 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11118 tooltip : 'This field is required'
11120 if (this.allowBlank ) {
11121 indicator.style = this.allowBlank ? ' display:none' : '';
11123 if (align ==='left' && this.fieldLabel.length) {
11125 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11132 cls : 'control-label col-form-label',
11133 html : this.fieldLabel
11144 var labelCfg = cfg.cn[1];
11145 var contentCfg = cfg.cn[2];
11147 if(this.indicatorpos == 'right'){
11152 cls : 'control-label col-form-label',
11156 html : this.fieldLabel
11170 labelCfg = cfg.cn[0];
11171 contentCfg = cfg.cn[1];
11175 if(this.labelWidth > 12){
11176 labelCfg.style = "width: " + this.labelWidth + 'px';
11179 if(this.labelWidth < 13 && this.labelmd == 0){
11180 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11183 if(this.labellg > 0){
11184 labelCfg.cls += ' col-lg-' + this.labellg;
11185 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11188 if(this.labelmd > 0){
11189 labelCfg.cls += ' col-md-' + this.labelmd;
11190 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11193 if(this.labelsm > 0){
11194 labelCfg.cls += ' col-sm-' + this.labelsm;
11195 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11198 if(this.labelxs > 0){
11199 labelCfg.cls += ' col-xs-' + this.labelxs;
11200 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11204 } else if ( this.fieldLabel.length) {
11211 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11212 tooltip : 'This field is required',
11213 style : this.allowBlank ? ' display:none' : ''
11217 //cls : 'input-group-addon',
11218 html : this.fieldLabel
11226 if(this.indicatorpos == 'right'){
11231 //cls : 'input-group-addon',
11232 html : this.fieldLabel
11237 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11238 tooltip : 'This field is required',
11239 style : this.allowBlank ? ' display:none' : ''
11259 if (this.parentType === 'Navbar' && this.parent().bar) {
11260 cfg.cls += ' navbar-form';
11263 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11264 // on BS4 we do this only if not form
11265 cfg.cls += ' navbar-form';
11273 * return the real input element.
11275 inputEl: function ()
11277 return this.el.select('input.form-control',true).first();
11280 tooltipEl : function()
11282 return this.inputEl();
11285 indicatorEl : function()
11287 if (Roo.bootstrap.version == 4) {
11288 return false; // not enabled in v4 yet.
11291 var indicator = this.el.select('i.roo-required-indicator',true).first();
11301 setDisabled : function(v)
11303 var i = this.inputEl().dom;
11305 i.removeAttribute('disabled');
11309 i.setAttribute('disabled','true');
11311 initEvents : function()
11314 this.inputEl().on("keydown" , this.fireKey, this);
11315 this.inputEl().on("focus", this.onFocus, this);
11316 this.inputEl().on("blur", this.onBlur, this);
11318 this.inputEl().relayEvent('keyup', this);
11319 this.inputEl().relayEvent('paste', this);
11321 this.indicator = this.indicatorEl();
11323 if(this.indicator){
11324 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11327 // reference to original value for reset
11328 this.originalValue = this.getValue();
11329 //Roo.form.TextField.superclass.initEvents.call(this);
11330 if(this.validationEvent == 'keyup'){
11331 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11332 this.inputEl().on('keyup', this.filterValidation, this);
11334 else if(this.validationEvent !== false){
11335 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11338 if(this.selectOnFocus){
11339 this.on("focus", this.preFocus, this);
11342 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11343 this.inputEl().on("keypress", this.filterKeys, this);
11345 this.inputEl().relayEvent('keypress', this);
11348 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11349 this.el.on("click", this.autoSize, this);
11352 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11353 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11356 if (typeof(this.before) == 'object') {
11357 this.before.render(this.el.select('.roo-input-before',true).first());
11359 if (typeof(this.after) == 'object') {
11360 this.after.render(this.el.select('.roo-input-after',true).first());
11363 this.inputEl().on('change', this.onChange, this);
11366 filterValidation : function(e){
11367 if(!e.isNavKeyPress()){
11368 this.validationTask.delay(this.validationDelay);
11372 * Validates the field value
11373 * @return {Boolean} True if the value is valid, else false
11375 validate : function(){
11376 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11377 if(this.disabled || this.validateValue(this.getRawValue())){
11382 this.markInvalid();
11388 * Validates a value according to the field's validation rules and marks the field as invalid
11389 * if the validation fails
11390 * @param {Mixed} value The value to validate
11391 * @return {Boolean} True if the value is valid, else false
11393 validateValue : function(value)
11395 if(this.getVisibilityEl().hasClass('hidden')){
11399 if(value.length < 1) { // if it's blank
11400 if(this.allowBlank){
11406 if(value.length < this.minLength){
11409 if(value.length > this.maxLength){
11413 var vt = Roo.form.VTypes;
11414 if(!vt[this.vtype](value, this)){
11418 if(typeof this.validator == "function"){
11419 var msg = this.validator(value);
11423 if (typeof(msg) == 'string') {
11424 this.invalidText = msg;
11428 if(this.regex && !this.regex.test(value)){
11436 fireKey : function(e){
11437 //Roo.log('field ' + e.getKey());
11438 if(e.isNavKeyPress()){
11439 this.fireEvent("specialkey", this, e);
11442 focus : function (selectText){
11444 this.inputEl().focus();
11445 if(selectText === true){
11446 this.inputEl().dom.select();
11452 onFocus : function(){
11453 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11454 // this.el.addClass(this.focusClass);
11456 if(!this.hasFocus){
11457 this.hasFocus = true;
11458 this.startValue = this.getValue();
11459 this.fireEvent("focus", this);
11463 beforeBlur : Roo.emptyFn,
11467 onBlur : function(){
11469 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11470 //this.el.removeClass(this.focusClass);
11472 this.hasFocus = false;
11473 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11476 var v = this.getValue();
11477 if(String(v) !== String(this.startValue)){
11478 this.fireEvent('change', this, v, this.startValue);
11480 this.fireEvent("blur", this);
11483 onChange : function(e)
11485 var v = this.getValue();
11486 if(String(v) !== String(this.startValue)){
11487 this.fireEvent('change', this, v, this.startValue);
11493 * Resets the current field value to the originally loaded value and clears any validation messages
11495 reset : function(){
11496 this.setValue(this.originalValue);
11500 * Returns the name of the field
11501 * @return {Mixed} name The name field
11503 getName: function(){
11507 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11508 * @return {Mixed} value The field value
11510 getValue : function(){
11512 var v = this.inputEl().getValue();
11517 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11518 * @return {Mixed} value The field value
11520 getRawValue : function(){
11521 var v = this.inputEl().getValue();
11527 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11528 * @param {Mixed} value The value to set
11530 setRawValue : function(v){
11531 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11534 selectText : function(start, end){
11535 var v = this.getRawValue();
11537 start = start === undefined ? 0 : start;
11538 end = end === undefined ? v.length : end;
11539 var d = this.inputEl().dom;
11540 if(d.setSelectionRange){
11541 d.setSelectionRange(start, end);
11542 }else if(d.createTextRange){
11543 var range = d.createTextRange();
11544 range.moveStart("character", start);
11545 range.moveEnd("character", v.length-end);
11552 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11553 * @param {Mixed} value The value to set
11555 setValue : function(v){
11558 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11564 processValue : function(value){
11565 if(this.stripCharsRe){
11566 var newValue = value.replace(this.stripCharsRe, '');
11567 if(newValue !== value){
11568 this.setRawValue(newValue);
11575 preFocus : function(){
11577 if(this.selectOnFocus){
11578 this.inputEl().dom.select();
11581 filterKeys : function(e){
11582 var k = e.getKey();
11583 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11586 var c = e.getCharCode(), cc = String.fromCharCode(c);
11587 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11590 if(!this.maskRe.test(cc)){
11595 * Clear any invalid styles/messages for this field
11597 clearInvalid : function(){
11599 if(!this.el || this.preventMark){ // not rendered
11604 this.el.removeClass([this.invalidClass, 'is-invalid']);
11606 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11608 var feedback = this.el.select('.form-control-feedback', true).first();
11611 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11616 if(this.indicator){
11617 this.indicator.removeClass('visible');
11618 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11621 this.fireEvent('valid', this);
11625 * Mark this field as valid
11627 markValid : function()
11629 if(!this.el || this.preventMark){ // not rendered...
11633 this.el.removeClass([this.invalidClass, this.validClass]);
11634 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11636 var feedback = this.el.select('.form-control-feedback', true).first();
11639 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11642 if(this.indicator){
11643 this.indicator.removeClass('visible');
11644 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11652 if(this.allowBlank && !this.getRawValue().length){
11655 if (Roo.bootstrap.version == 3) {
11656 this.el.addClass(this.validClass);
11658 this.inputEl().addClass('is-valid');
11661 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11663 var feedback = this.el.select('.form-control-feedback', true).first();
11666 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11667 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11672 this.fireEvent('valid', this);
11676 * Mark this field as invalid
11677 * @param {String} msg The validation message
11679 markInvalid : function(msg)
11681 if(!this.el || this.preventMark){ // not rendered
11685 this.el.removeClass([this.invalidClass, this.validClass]);
11686 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11688 var feedback = this.el.select('.form-control-feedback', true).first();
11691 this.el.select('.form-control-feedback', true).first().removeClass(
11692 [this.invalidFeedbackClass, this.validFeedbackClass]);
11699 if(this.allowBlank && !this.getRawValue().length){
11703 if(this.indicator){
11704 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11705 this.indicator.addClass('visible');
11707 if (Roo.bootstrap.version == 3) {
11708 this.el.addClass(this.invalidClass);
11710 this.inputEl().addClass('is-invalid');
11715 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11717 var feedback = this.el.select('.form-control-feedback', true).first();
11720 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11722 if(this.getValue().length || this.forceFeedback){
11723 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11730 this.fireEvent('invalid', this, msg);
11733 SafariOnKeyDown : function(event)
11735 // this is a workaround for a password hang bug on chrome/ webkit.
11736 if (this.inputEl().dom.type != 'password') {
11740 var isSelectAll = false;
11742 if(this.inputEl().dom.selectionEnd > 0){
11743 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11745 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11746 event.preventDefault();
11751 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11753 event.preventDefault();
11754 // this is very hacky as keydown always get's upper case.
11756 var cc = String.fromCharCode(event.getCharCode());
11757 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11761 adjustWidth : function(tag, w){
11762 tag = tag.toLowerCase();
11763 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11764 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11765 if(tag == 'input'){
11768 if(tag == 'textarea'){
11771 }else if(Roo.isOpera){
11772 if(tag == 'input'){
11775 if(tag == 'textarea'){
11783 setFieldLabel : function(v)
11785 if(!this.rendered){
11789 if(this.indicatorEl()){
11790 var ar = this.el.select('label > span',true);
11792 if (ar.elements.length) {
11793 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11794 this.fieldLabel = v;
11798 var br = this.el.select('label',true);
11800 if(br.elements.length) {
11801 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11802 this.fieldLabel = v;
11806 Roo.log('Cannot Found any of label > span || label in input');
11810 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11811 this.fieldLabel = v;
11826 * @class Roo.bootstrap.TextArea
11827 * @extends Roo.bootstrap.Input
11828 * Bootstrap TextArea class
11829 * @cfg {Number} cols Specifies the visible width of a text area
11830 * @cfg {Number} rows Specifies the visible number of lines in a text area
11831 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11832 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11833 * @cfg {string} html text
11836 * Create a new TextArea
11837 * @param {Object} config The config object
11840 Roo.bootstrap.TextArea = function(config){
11841 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11845 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11855 getAutoCreate : function(){
11857 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11863 if(this.inputType != 'hidden'){
11864 cfg.cls = 'form-group' //input-group
11872 value : this.value || '',
11873 html: this.html || '',
11874 cls : 'form-control',
11875 placeholder : this.placeholder || ''
11879 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11880 input.maxLength = this.maxLength;
11884 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11888 input.cols = this.cols;
11891 if (this.readOnly) {
11892 input.readonly = true;
11896 input.name = this.name;
11900 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11904 ['xs','sm','md','lg'].map(function(size){
11905 if (settings[size]) {
11906 cfg.cls += ' col-' + size + '-' + settings[size];
11910 var inputblock = input;
11912 if(this.hasFeedback && !this.allowBlank){
11916 cls: 'glyphicon form-control-feedback'
11920 cls : 'has-feedback',
11929 if (this.before || this.after) {
11932 cls : 'input-group',
11936 inputblock.cn.push({
11938 cls : 'input-group-addon',
11943 inputblock.cn.push(input);
11945 if(this.hasFeedback && !this.allowBlank){
11946 inputblock.cls += ' has-feedback';
11947 inputblock.cn.push(feedback);
11951 inputblock.cn.push({
11953 cls : 'input-group-addon',
11960 if (align ==='left' && this.fieldLabel.length) {
11965 cls : 'control-label',
11966 html : this.fieldLabel
11977 if(this.labelWidth > 12){
11978 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11981 if(this.labelWidth < 13 && this.labelmd == 0){
11982 this.labelmd = this.labelWidth;
11985 if(this.labellg > 0){
11986 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11987 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11990 if(this.labelmd > 0){
11991 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11992 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11995 if(this.labelsm > 0){
11996 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11997 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12000 if(this.labelxs > 0){
12001 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12002 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12005 } else if ( this.fieldLabel.length) {
12010 //cls : 'input-group-addon',
12011 html : this.fieldLabel
12029 if (this.disabled) {
12030 input.disabled=true;
12037 * return the real textarea element.
12039 inputEl: function ()
12041 return this.el.select('textarea.form-control',true).first();
12045 * Clear any invalid styles/messages for this field
12047 clearInvalid : function()
12050 if(!this.el || this.preventMark){ // not rendered
12054 var label = this.el.select('label', true).first();
12055 var icon = this.el.select('i.fa-star', true).first();
12060 this.el.removeClass( this.validClass);
12061 this.inputEl().removeClass('is-invalid');
12063 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12065 var feedback = this.el.select('.form-control-feedback', true).first();
12068 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12073 this.fireEvent('valid', this);
12077 * Mark this field as valid
12079 markValid : function()
12081 if(!this.el || this.preventMark){ // not rendered
12085 this.el.removeClass([this.invalidClass, this.validClass]);
12086 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12088 var feedback = this.el.select('.form-control-feedback', true).first();
12091 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12094 if(this.disabled || this.allowBlank){
12098 var label = this.el.select('label', true).first();
12099 var icon = this.el.select('i.fa-star', true).first();
12104 if (Roo.bootstrap.version == 3) {
12105 this.el.addClass(this.validClass);
12107 this.inputEl().addClass('is-valid');
12111 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12113 var feedback = this.el.select('.form-control-feedback', true).first();
12116 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12117 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12122 this.fireEvent('valid', this);
12126 * Mark this field as invalid
12127 * @param {String} msg The validation message
12129 markInvalid : function(msg)
12131 if(!this.el || this.preventMark){ // not rendered
12135 this.el.removeClass([this.invalidClass, this.validClass]);
12136 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12138 var feedback = this.el.select('.form-control-feedback', true).first();
12141 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12144 if(this.disabled || this.allowBlank){
12148 var label = this.el.select('label', true).first();
12149 var icon = this.el.select('i.fa-star', true).first();
12151 if(!this.getValue().length && label && !icon){
12152 this.el.createChild({
12154 cls : 'text-danger fa fa-lg fa-star',
12155 tooltip : 'This field is required',
12156 style : 'margin-right:5px;'
12160 if (Roo.bootstrap.version == 3) {
12161 this.el.addClass(this.invalidClass);
12163 this.inputEl().addClass('is-invalid');
12166 // fixme ... this may be depricated need to test..
12167 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12169 var feedback = this.el.select('.form-control-feedback', true).first();
12172 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12174 if(this.getValue().length || this.forceFeedback){
12175 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12182 this.fireEvent('invalid', this, msg);
12190 * trigger field - base class for combo..
12195 * @class Roo.bootstrap.TriggerField
12196 * @extends Roo.bootstrap.Input
12197 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12198 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12199 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12200 * for which you can provide a custom implementation. For example:
12202 var trigger = new Roo.bootstrap.TriggerField();
12203 trigger.onTriggerClick = myTriggerFn;
12204 trigger.applyTo('my-field');
12207 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12208 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12209 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12210 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12211 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12214 * Create a new TriggerField.
12215 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12216 * to the base TextField)
12218 Roo.bootstrap.TriggerField = function(config){
12219 this.mimicing = false;
12220 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12223 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12225 * @cfg {String} triggerClass A CSS class to apply to the trigger
12228 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12233 * @cfg {Boolean} removable (true|false) special filter default false
12237 /** @cfg {Boolean} grow @hide */
12238 /** @cfg {Number} growMin @hide */
12239 /** @cfg {Number} growMax @hide */
12245 autoSize: Roo.emptyFn,
12249 deferHeight : true,
12252 actionMode : 'wrap',
12257 getAutoCreate : function(){
12259 var align = this.labelAlign || this.parentLabelAlign();
12264 cls: 'form-group' //input-group
12271 type : this.inputType,
12272 cls : 'form-control',
12273 autocomplete: 'new-password',
12274 placeholder : this.placeholder || ''
12278 input.name = this.name;
12281 input.cls += ' input-' + this.size;
12284 if (this.disabled) {
12285 input.disabled=true;
12288 var inputblock = input;
12290 if(this.hasFeedback && !this.allowBlank){
12294 cls: 'glyphicon form-control-feedback'
12297 if(this.removable && !this.editable ){
12299 cls : 'has-feedback',
12305 cls : 'roo-combo-removable-btn close'
12312 cls : 'has-feedback',
12321 if(this.removable && !this.editable ){
12323 cls : 'roo-removable',
12329 cls : 'roo-combo-removable-btn close'
12336 if (this.before || this.after) {
12339 cls : 'input-group',
12343 inputblock.cn.push({
12345 cls : 'input-group-addon input-group-prepend input-group-text',
12350 inputblock.cn.push(input);
12352 if(this.hasFeedback && !this.allowBlank){
12353 inputblock.cls += ' has-feedback';
12354 inputblock.cn.push(feedback);
12358 inputblock.cn.push({
12360 cls : 'input-group-addon input-group-append input-group-text',
12369 var ibwrap = inputblock;
12374 cls: 'roo-select2-choices',
12378 cls: 'roo-select2-search-field',
12390 cls: 'roo-select2-container input-group',
12395 cls: 'form-hidden-field'
12401 if(!this.multiple && this.showToggleBtn){
12407 if (this.caret != false) {
12410 cls: 'fa fa-' + this.caret
12417 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12419 Roo.bootstrap.version == 3 ? caret : '',
12422 cls: 'combobox-clear',
12436 combobox.cls += ' roo-select2-container-multi';
12440 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12441 tooltip : 'This field is required'
12443 if (Roo.bootstrap.version == 4) {
12446 style : 'display:none'
12451 if (align ==='left' && this.fieldLabel.length) {
12453 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12460 cls : 'control-label',
12461 html : this.fieldLabel
12473 var labelCfg = cfg.cn[1];
12474 var contentCfg = cfg.cn[2];
12476 if(this.indicatorpos == 'right'){
12481 cls : 'control-label',
12485 html : this.fieldLabel
12499 labelCfg = cfg.cn[0];
12500 contentCfg = cfg.cn[1];
12503 if(this.labelWidth > 12){
12504 labelCfg.style = "width: " + this.labelWidth + 'px';
12507 if(this.labelWidth < 13 && this.labelmd == 0){
12508 this.labelmd = this.labelWidth;
12511 if(this.labellg > 0){
12512 labelCfg.cls += ' col-lg-' + this.labellg;
12513 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12516 if(this.labelmd > 0){
12517 labelCfg.cls += ' col-md-' + this.labelmd;
12518 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12521 if(this.labelsm > 0){
12522 labelCfg.cls += ' col-sm-' + this.labelsm;
12523 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12526 if(this.labelxs > 0){
12527 labelCfg.cls += ' col-xs-' + this.labelxs;
12528 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12531 } else if ( this.fieldLabel.length) {
12532 // Roo.log(" label");
12537 //cls : 'input-group-addon',
12538 html : this.fieldLabel
12546 if(this.indicatorpos == 'right'){
12554 html : this.fieldLabel
12568 // Roo.log(" no label && no align");
12575 ['xs','sm','md','lg'].map(function(size){
12576 if (settings[size]) {
12577 cfg.cls += ' col-' + size + '-' + settings[size];
12588 onResize : function(w, h){
12589 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12590 // if(typeof w == 'number'){
12591 // var x = w - this.trigger.getWidth();
12592 // this.inputEl().setWidth(this.adjustWidth('input', x));
12593 // this.trigger.setStyle('left', x+'px');
12598 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12601 getResizeEl : function(){
12602 return this.inputEl();
12606 getPositionEl : function(){
12607 return this.inputEl();
12611 alignErrorIcon : function(){
12612 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12616 initEvents : function(){
12620 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12621 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12622 if(!this.multiple && this.showToggleBtn){
12623 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12624 if(this.hideTrigger){
12625 this.trigger.setDisplayed(false);
12627 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12631 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12634 if(this.removable && !this.editable && !this.tickable){
12635 var close = this.closeTriggerEl();
12638 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12639 close.on('click', this.removeBtnClick, this, close);
12643 //this.trigger.addClassOnOver('x-form-trigger-over');
12644 //this.trigger.addClassOnClick('x-form-trigger-click');
12647 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12651 closeTriggerEl : function()
12653 var close = this.el.select('.roo-combo-removable-btn', true).first();
12654 return close ? close : false;
12657 removeBtnClick : function(e, h, el)
12659 e.preventDefault();
12661 if(this.fireEvent("remove", this) !== false){
12663 this.fireEvent("afterremove", this)
12667 createList : function()
12669 this.list = Roo.get(document.body).createChild({
12670 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12671 cls: 'typeahead typeahead-long dropdown-menu shadow',
12672 style: 'display:none'
12675 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12680 initTrigger : function(){
12685 onDestroy : function(){
12687 this.trigger.removeAllListeners();
12688 // this.trigger.remove();
12691 // this.wrap.remove();
12693 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12697 onFocus : function(){
12698 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12700 if(!this.mimicing){
12701 this.wrap.addClass('x-trigger-wrap-focus');
12702 this.mimicing = true;
12703 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12704 if(this.monitorTab){
12705 this.el.on("keydown", this.checkTab, this);
12712 checkTab : function(e){
12713 if(e.getKey() == e.TAB){
12714 this.triggerBlur();
12719 onBlur : function(){
12724 mimicBlur : function(e, t){
12726 if(!this.wrap.contains(t) && this.validateBlur()){
12727 this.triggerBlur();
12733 triggerBlur : function(){
12734 this.mimicing = false;
12735 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12736 if(this.monitorTab){
12737 this.el.un("keydown", this.checkTab, this);
12739 //this.wrap.removeClass('x-trigger-wrap-focus');
12740 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12744 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12745 validateBlur : function(e, t){
12750 onDisable : function(){
12751 this.inputEl().dom.disabled = true;
12752 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12754 // this.wrap.addClass('x-item-disabled');
12759 onEnable : function(){
12760 this.inputEl().dom.disabled = false;
12761 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12763 // this.el.removeClass('x-item-disabled');
12768 onShow : function(){
12769 var ae = this.getActionEl();
12772 ae.dom.style.display = '';
12773 ae.dom.style.visibility = 'visible';
12779 onHide : function(){
12780 var ae = this.getActionEl();
12781 ae.dom.style.display = 'none';
12785 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12786 * by an implementing function.
12788 * @param {EventObject} e
12790 onTriggerClick : Roo.emptyFn
12798 * @class Roo.bootstrap.CardUploader
12799 * @extends Roo.bootstrap.Button
12800 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12801 * @cfg {Number} errorTimeout default 3000
12802 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12803 * @cfg {Array} html The button text.
12807 * Create a new CardUploader
12808 * @param {Object} config The config object
12811 Roo.bootstrap.CardUploader = function(config){
12815 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12818 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12826 * When a image is clicked on - and needs to display a slideshow or similar..
12827 * @param {Roo.bootstrap.Card} this
12828 * @param {Object} The image information data
12834 * When a the download link is clicked
12835 * @param {Roo.bootstrap.Card} this
12836 * @param {Object} The image information data contains
12843 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12846 errorTimeout : 3000,
12850 fileCollection : false,
12853 getAutoCreate : function()
12857 cls :'form-group' ,
12862 //cls : 'input-group-addon',
12863 html : this.fieldLabel
12871 value : this.value,
12872 cls : 'd-none form-control'
12877 multiple : 'multiple',
12879 cls : 'd-none roo-card-upload-selector'
12883 cls : 'roo-card-uploader-button-container w-100 mb-2'
12886 cls : 'card-columns roo-card-uploader-container'
12896 getChildContainer : function() /// what children are added to.
12898 return this.containerEl;
12901 getButtonContainer : function() /// what children are added to.
12903 return this.el.select(".roo-card-uploader-button-container").first();
12906 initEvents : function()
12909 Roo.bootstrap.Input.prototype.initEvents.call(this);
12913 xns: Roo.bootstrap,
12916 container_method : 'getButtonContainer' ,
12917 html : this.html, // fix changable?
12920 'click' : function(btn, e) {
12929 this.urlAPI = (window.createObjectURL && window) ||
12930 (window.URL && URL.revokeObjectURL && URL) ||
12931 (window.webkitURL && webkitURL);
12936 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12938 this.selectorEl.on('change', this.onFileSelected, this);
12941 this.images.forEach(function(img) {
12944 this.images = false;
12946 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12952 onClick : function(e)
12954 e.preventDefault();
12956 this.selectorEl.dom.click();
12960 onFileSelected : function(e)
12962 e.preventDefault();
12964 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12968 Roo.each(this.selectorEl.dom.files, function(file){
12969 this.addFile(file);
12978 addFile : function(file)
12981 if(typeof(file) === 'string'){
12982 throw "Add file by name?"; // should not happen
12986 if(!file || !this.urlAPI){
12996 var url = _this.urlAPI.createObjectURL( file);
12999 id : Roo.bootstrap.CardUploader.ID--,
13000 is_uploaded : false,
13004 mimetype : file.type,
13012 * addCard - add an Attachment to the uploader
13013 * @param data - the data about the image to upload
13017 title : "Title of file",
13018 is_uploaded : false,
13019 src : "http://.....",
13020 srcfile : { the File upload object },
13021 mimetype : file.type,
13024 .. any other data...
13030 addCard : function (data)
13032 // hidden input element?
13033 // if the file is not an image...
13034 //then we need to use something other that and header_image
13039 xns : Roo.bootstrap,
13040 xtype : 'CardFooter',
13043 xns : Roo.bootstrap,
13049 xns : Roo.bootstrap,
13051 html : String.format("<small>{0}</small>", data.title),
13052 cls : 'col-10 text-left',
13057 click : function() {
13059 t.fireEvent( "download", t, data );
13065 xns : Roo.bootstrap,
13067 style: 'max-height: 28px; ',
13073 click : function() {
13074 t.removeCard(data.id)
13086 var cn = this.addxtype(
13089 xns : Roo.bootstrap,
13092 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13093 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13094 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13099 initEvents : function() {
13100 Roo.bootstrap.Card.prototype.initEvents.call(this);
13102 this.imgEl = this.el.select('.card-img-top').first();
13104 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13105 this.imgEl.set({ 'pointer' : 'cursor' });
13108 this.getCardFooter().addClass('p-1');
13115 // dont' really need ot update items.
13116 // this.items.push(cn);
13117 this.fileCollection.add(cn);
13119 if (!data.srcfile) {
13120 this.updateInput();
13125 var reader = new FileReader();
13126 reader.addEventListener("load", function() {
13127 data.srcdata = reader.result;
13130 reader.readAsDataURL(data.srcfile);
13135 removeCard : function(id)
13138 var card = this.fileCollection.get(id);
13139 card.data.is_deleted = 1;
13140 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13141 //this.fileCollection.remove(card);
13142 //this.items = this.items.filter(function(e) { return e != card });
13143 // dont' really need ot update items.
13144 card.el.dom.parentNode.removeChild(card.el.dom);
13145 this.updateInput();
13151 this.fileCollection.each(function(card) {
13152 if (card.el.dom && card.el.dom.parentNode) {
13153 card.el.dom.parentNode.removeChild(card.el.dom);
13156 this.fileCollection.clear();
13157 this.updateInput();
13160 updateInput : function()
13163 this.fileCollection.each(function(e) {
13167 this.inputEl().dom.value = JSON.stringify(data);
13177 Roo.bootstrap.CardUploader.ID = -1;/*
13179 * Ext JS Library 1.1.1
13180 * Copyright(c) 2006-2007, Ext JS, LLC.
13182 * Originally Released Under LGPL - original licence link has changed is not relivant.
13185 * <script type="text/javascript">
13190 * @class Roo.data.SortTypes
13192 * Defines the default sorting (casting?) comparison functions used when sorting data.
13194 Roo.data.SortTypes = {
13196 * Default sort that does nothing
13197 * @param {Mixed} s The value being converted
13198 * @return {Mixed} The comparison value
13200 none : function(s){
13205 * The regular expression used to strip tags
13209 stripTagsRE : /<\/?[^>]+>/gi,
13212 * Strips all HTML tags to sort on text only
13213 * @param {Mixed} s The value being converted
13214 * @return {String} The comparison value
13216 asText : function(s){
13217 return String(s).replace(this.stripTagsRE, "");
13221 * Strips all HTML tags to sort on text only - Case insensitive
13222 * @param {Mixed} s The value being converted
13223 * @return {String} The comparison value
13225 asUCText : function(s){
13226 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13230 * Case insensitive string
13231 * @param {Mixed} s The value being converted
13232 * @return {String} The comparison value
13234 asUCString : function(s) {
13235 return String(s).toUpperCase();
13240 * @param {Mixed} s The value being converted
13241 * @return {Number} The comparison value
13243 asDate : function(s) {
13247 if(s instanceof Date){
13248 return s.getTime();
13250 return Date.parse(String(s));
13255 * @param {Mixed} s The value being converted
13256 * @return {Float} The comparison value
13258 asFloat : function(s) {
13259 var val = parseFloat(String(s).replace(/,/g, ""));
13268 * @param {Mixed} s The value being converted
13269 * @return {Number} The comparison value
13271 asInt : function(s) {
13272 var val = parseInt(String(s).replace(/,/g, ""));
13280 * Ext JS Library 1.1.1
13281 * Copyright(c) 2006-2007, Ext JS, LLC.
13283 * Originally Released Under LGPL - original licence link has changed is not relivant.
13286 * <script type="text/javascript">
13290 * @class Roo.data.Record
13291 * Instances of this class encapsulate both record <em>definition</em> information, and record
13292 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13293 * to access Records cached in an {@link Roo.data.Store} object.<br>
13295 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13296 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13299 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13301 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13302 * {@link #create}. The parameters are the same.
13303 * @param {Array} data An associative Array of data values keyed by the field name.
13304 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13305 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13306 * not specified an integer id is generated.
13308 Roo.data.Record = function(data, id){
13309 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13314 * Generate a constructor for a specific record layout.
13315 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13316 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13317 * Each field definition object may contain the following properties: <ul>
13318 * <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,
13319 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13320 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13321 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13322 * is being used, then this is a string containing the javascript expression to reference the data relative to
13323 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13324 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13325 * this may be omitted.</p></li>
13326 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13327 * <ul><li>auto (Default, implies no conversion)</li>
13332 * <li>date</li></ul></p></li>
13333 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13334 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13335 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13336 * by the Reader into an object that will be stored in the Record. It is passed the
13337 * following parameters:<ul>
13338 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13340 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13342 * <br>usage:<br><pre><code>
13343 var TopicRecord = Roo.data.Record.create(
13344 {name: 'title', mapping: 'topic_title'},
13345 {name: 'author', mapping: 'username'},
13346 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13347 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13348 {name: 'lastPoster', mapping: 'user2'},
13349 {name: 'excerpt', mapping: 'post_text'}
13352 var myNewRecord = new TopicRecord({
13353 title: 'Do my job please',
13356 lastPost: new Date(),
13357 lastPoster: 'Animal',
13358 excerpt: 'No way dude!'
13360 myStore.add(myNewRecord);
13365 Roo.data.Record.create = function(o){
13366 var f = function(){
13367 f.superclass.constructor.apply(this, arguments);
13369 Roo.extend(f, Roo.data.Record);
13370 var p = f.prototype;
13371 p.fields = new Roo.util.MixedCollection(false, function(field){
13374 for(var i = 0, len = o.length; i < len; i++){
13375 p.fields.add(new Roo.data.Field(o[i]));
13377 f.getField = function(name){
13378 return p.fields.get(name);
13383 Roo.data.Record.AUTO_ID = 1000;
13384 Roo.data.Record.EDIT = 'edit';
13385 Roo.data.Record.REJECT = 'reject';
13386 Roo.data.Record.COMMIT = 'commit';
13388 Roo.data.Record.prototype = {
13390 * Readonly flag - true if this record has been modified.
13399 join : function(store){
13400 this.store = store;
13404 * Set the named field to the specified value.
13405 * @param {String} name The name of the field to set.
13406 * @param {Object} value The value to set the field to.
13408 set : function(name, value){
13409 if(this.data[name] == value){
13413 if(!this.modified){
13414 this.modified = {};
13416 if(typeof this.modified[name] == 'undefined'){
13417 this.modified[name] = this.data[name];
13419 this.data[name] = value;
13420 if(!this.editing && this.store){
13421 this.store.afterEdit(this);
13426 * Get the value of the named field.
13427 * @param {String} name The name of the field to get the value of.
13428 * @return {Object} The value of the field.
13430 get : function(name){
13431 return this.data[name];
13435 beginEdit : function(){
13436 this.editing = true;
13437 this.modified = {};
13441 cancelEdit : function(){
13442 this.editing = false;
13443 delete this.modified;
13447 endEdit : function(){
13448 this.editing = false;
13449 if(this.dirty && this.store){
13450 this.store.afterEdit(this);
13455 * Usually called by the {@link Roo.data.Store} which owns the Record.
13456 * Rejects all changes made to the Record since either creation, or the last commit operation.
13457 * Modified fields are reverted to their original values.
13459 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13460 * of reject operations.
13462 reject : function(){
13463 var m = this.modified;
13465 if(typeof m[n] != "function"){
13466 this.data[n] = m[n];
13469 this.dirty = false;
13470 delete this.modified;
13471 this.editing = false;
13473 this.store.afterReject(this);
13478 * Usually called by the {@link Roo.data.Store} which owns the Record.
13479 * Commits all changes made to the Record since either creation, or the last commit operation.
13481 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13482 * of commit operations.
13484 commit : function(){
13485 this.dirty = false;
13486 delete this.modified;
13487 this.editing = false;
13489 this.store.afterCommit(this);
13494 hasError : function(){
13495 return this.error != null;
13499 clearError : function(){
13504 * Creates a copy of this record.
13505 * @param {String} id (optional) A new record id if you don't want to use this record's id
13508 copy : function(newId) {
13509 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13513 * Ext JS Library 1.1.1
13514 * Copyright(c) 2006-2007, Ext JS, LLC.
13516 * Originally Released Under LGPL - original licence link has changed is not relivant.
13519 * <script type="text/javascript">
13525 * @class Roo.data.Store
13526 * @extends Roo.util.Observable
13527 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13528 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13530 * 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
13531 * has no knowledge of the format of the data returned by the Proxy.<br>
13533 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13534 * instances from the data object. These records are cached and made available through accessor functions.
13536 * Creates a new Store.
13537 * @param {Object} config A config object containing the objects needed for the Store to access data,
13538 * and read the data into Records.
13540 Roo.data.Store = function(config){
13541 this.data = new Roo.util.MixedCollection(false);
13542 this.data.getKey = function(o){
13545 this.baseParams = {};
13547 this.paramNames = {
13552 "multisort" : "_multisort"
13555 if(config && config.data){
13556 this.inlineData = config.data;
13557 delete config.data;
13560 Roo.apply(this, config);
13562 if(this.reader){ // reader passed
13563 this.reader = Roo.factory(this.reader, Roo.data);
13564 this.reader.xmodule = this.xmodule || false;
13565 if(!this.recordType){
13566 this.recordType = this.reader.recordType;
13568 if(this.reader.onMetaChange){
13569 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13573 if(this.recordType){
13574 this.fields = this.recordType.prototype.fields;
13576 this.modified = [];
13580 * @event datachanged
13581 * Fires when the data cache has changed, and a widget which is using this Store
13582 * as a Record cache should refresh its view.
13583 * @param {Store} this
13585 datachanged : true,
13587 * @event metachange
13588 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13589 * @param {Store} this
13590 * @param {Object} meta The JSON metadata
13595 * Fires when Records have been added to the Store
13596 * @param {Store} this
13597 * @param {Roo.data.Record[]} records The array of Records added
13598 * @param {Number} index The index at which the record(s) were added
13603 * Fires when a Record has been removed from the Store
13604 * @param {Store} this
13605 * @param {Roo.data.Record} record The Record that was removed
13606 * @param {Number} index The index at which the record was removed
13611 * Fires when a Record has been updated
13612 * @param {Store} this
13613 * @param {Roo.data.Record} record The Record that was updated
13614 * @param {String} operation The update operation being performed. Value may be one of:
13616 Roo.data.Record.EDIT
13617 Roo.data.Record.REJECT
13618 Roo.data.Record.COMMIT
13624 * Fires when the data cache has been cleared.
13625 * @param {Store} this
13629 * @event beforeload
13630 * Fires before a request is made for a new data object. If the beforeload handler returns false
13631 * the load action will be canceled.
13632 * @param {Store} this
13633 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13637 * @event beforeloadadd
13638 * Fires after a new set of Records has been loaded.
13639 * @param {Store} this
13640 * @param {Roo.data.Record[]} records The Records that were loaded
13641 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13643 beforeloadadd : true,
13646 * Fires after a new set of Records has been loaded, before they are added to the store.
13647 * @param {Store} this
13648 * @param {Roo.data.Record[]} records The Records that were loaded
13649 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13650 * @params {Object} return from reader
13654 * @event loadexception
13655 * Fires if an exception occurs in the Proxy during loading.
13656 * Called with the signature of the Proxy's "loadexception" event.
13657 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13660 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13661 * @param {Object} load options
13662 * @param {Object} jsonData from your request (normally this contains the Exception)
13664 loadexception : true
13668 this.proxy = Roo.factory(this.proxy, Roo.data);
13669 this.proxy.xmodule = this.xmodule || false;
13670 this.relayEvents(this.proxy, ["loadexception"]);
13672 this.sortToggle = {};
13673 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13675 Roo.data.Store.superclass.constructor.call(this);
13677 if(this.inlineData){
13678 this.loadData(this.inlineData);
13679 delete this.inlineData;
13683 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13685 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13686 * without a remote query - used by combo/forms at present.
13690 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13693 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13696 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13697 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13700 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13701 * on any HTTP request
13704 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13707 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13711 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13712 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13714 remoteSort : false,
13717 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13718 * loaded or when a record is removed. (defaults to false).
13720 pruneModifiedRecords : false,
13723 lastOptions : null,
13726 * Add Records to the Store and fires the add event.
13727 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13729 add : function(records){
13730 records = [].concat(records);
13731 for(var i = 0, len = records.length; i < len; i++){
13732 records[i].join(this);
13734 var index = this.data.length;
13735 this.data.addAll(records);
13736 this.fireEvent("add", this, records, index);
13740 * Remove a Record from the Store and fires the remove event.
13741 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13743 remove : function(record){
13744 var index = this.data.indexOf(record);
13745 this.data.removeAt(index);
13747 if(this.pruneModifiedRecords){
13748 this.modified.remove(record);
13750 this.fireEvent("remove", this, record, index);
13754 * Remove all Records from the Store and fires the clear event.
13756 removeAll : function(){
13758 if(this.pruneModifiedRecords){
13759 this.modified = [];
13761 this.fireEvent("clear", this);
13765 * Inserts Records to the Store at the given index and fires the add event.
13766 * @param {Number} index The start index at which to insert the passed Records.
13767 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13769 insert : function(index, records){
13770 records = [].concat(records);
13771 for(var i = 0, len = records.length; i < len; i++){
13772 this.data.insert(index, records[i]);
13773 records[i].join(this);
13775 this.fireEvent("add", this, records, index);
13779 * Get the index within the cache of the passed Record.
13780 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13781 * @return {Number} The index of the passed Record. Returns -1 if not found.
13783 indexOf : function(record){
13784 return this.data.indexOf(record);
13788 * Get the index within the cache of the Record with the passed id.
13789 * @param {String} id The id of the Record to find.
13790 * @return {Number} The index of the Record. Returns -1 if not found.
13792 indexOfId : function(id){
13793 return this.data.indexOfKey(id);
13797 * Get the Record with the specified id.
13798 * @param {String} id The id of the Record to find.
13799 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13801 getById : function(id){
13802 return this.data.key(id);
13806 * Get the Record at the specified index.
13807 * @param {Number} index The index of the Record to find.
13808 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13810 getAt : function(index){
13811 return this.data.itemAt(index);
13815 * Returns a range of Records between specified indices.
13816 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13817 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13818 * @return {Roo.data.Record[]} An array of Records
13820 getRange : function(start, end){
13821 return this.data.getRange(start, end);
13825 storeOptions : function(o){
13826 o = Roo.apply({}, o);
13829 this.lastOptions = o;
13833 * Loads the Record cache from the configured Proxy using the configured Reader.
13835 * If using remote paging, then the first load call must specify the <em>start</em>
13836 * and <em>limit</em> properties in the options.params property to establish the initial
13837 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13839 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13840 * and this call will return before the new data has been loaded. Perform any post-processing
13841 * in a callback function, or in a "load" event handler.</strong>
13843 * @param {Object} options An object containing properties which control loading options:<ul>
13844 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13845 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13846 * passed the following arguments:<ul>
13847 * <li>r : Roo.data.Record[]</li>
13848 * <li>options: Options object from the load call</li>
13849 * <li>success: Boolean success indicator</li></ul></li>
13850 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13851 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13854 load : function(options){
13855 options = options || {};
13856 if(this.fireEvent("beforeload", this, options) !== false){
13857 this.storeOptions(options);
13858 var p = Roo.apply(options.params || {}, this.baseParams);
13859 // if meta was not loaded from remote source.. try requesting it.
13860 if (!this.reader.metaFromRemote) {
13861 p._requestMeta = 1;
13863 if(this.sortInfo && this.remoteSort){
13864 var pn = this.paramNames;
13865 p[pn["sort"]] = this.sortInfo.field;
13866 p[pn["dir"]] = this.sortInfo.direction;
13868 if (this.multiSort) {
13869 var pn = this.paramNames;
13870 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13873 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13878 * Reloads the Record cache from the configured Proxy using the configured Reader and
13879 * the options from the last load operation performed.
13880 * @param {Object} options (optional) An object containing properties which may override the options
13881 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13882 * the most recently used options are reused).
13884 reload : function(options){
13885 this.load(Roo.applyIf(options||{}, this.lastOptions));
13889 // Called as a callback by the Reader during a load operation.
13890 loadRecords : function(o, options, success){
13891 if(!o || success === false){
13892 if(success !== false){
13893 this.fireEvent("load", this, [], options, o);
13895 if(options.callback){
13896 options.callback.call(options.scope || this, [], options, false);
13900 // if data returned failure - throw an exception.
13901 if (o.success === false) {
13902 // show a message if no listener is registered.
13903 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13904 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13906 // loadmask wil be hooked into this..
13907 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13910 var r = o.records, t = o.totalRecords || r.length;
13912 this.fireEvent("beforeloadadd", this, r, options, o);
13914 if(!options || options.add !== true){
13915 if(this.pruneModifiedRecords){
13916 this.modified = [];
13918 for(var i = 0, len = r.length; i < len; i++){
13922 this.data = this.snapshot;
13923 delete this.snapshot;
13926 this.data.addAll(r);
13927 this.totalLength = t;
13929 this.fireEvent("datachanged", this);
13931 this.totalLength = Math.max(t, this.data.length+r.length);
13935 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13937 var e = new Roo.data.Record({});
13939 e.set(this.parent.displayField, this.parent.emptyTitle);
13940 e.set(this.parent.valueField, '');
13945 this.fireEvent("load", this, r, options, o);
13946 if(options.callback){
13947 options.callback.call(options.scope || this, r, options, true);
13953 * Loads data from a passed data block. A Reader which understands the format of the data
13954 * must have been configured in the constructor.
13955 * @param {Object} data The data block from which to read the Records. The format of the data expected
13956 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13957 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13959 loadData : function(o, append){
13960 var r = this.reader.readRecords(o);
13961 this.loadRecords(r, {add: append}, true);
13965 * using 'cn' the nested child reader read the child array into it's child stores.
13966 * @param {Object} rec The record with a 'children array
13968 loadDataFromChildren : function(rec)
13970 this.loadData(this.reader.toLoadData(rec));
13975 * Gets the number of cached records.
13977 * <em>If using paging, this may not be the total size of the dataset. If the data object
13978 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13979 * the data set size</em>
13981 getCount : function(){
13982 return this.data.length || 0;
13986 * Gets the total number of records in the dataset as returned by the server.
13988 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13989 * the dataset size</em>
13991 getTotalCount : function(){
13992 return this.totalLength || 0;
13996 * Returns the sort state of the Store as an object with two properties:
13998 field {String} The name of the field by which the Records are sorted
13999 direction {String} The sort order, "ASC" or "DESC"
14002 getSortState : function(){
14003 return this.sortInfo;
14007 applySort : function(){
14008 if(this.sortInfo && !this.remoteSort){
14009 var s = this.sortInfo, f = s.field;
14010 var st = this.fields.get(f).sortType;
14011 var fn = function(r1, r2){
14012 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14013 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14015 this.data.sort(s.direction, fn);
14016 if(this.snapshot && this.snapshot != this.data){
14017 this.snapshot.sort(s.direction, fn);
14023 * Sets the default sort column and order to be used by the next load operation.
14024 * @param {String} fieldName The name of the field to sort by.
14025 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14027 setDefaultSort : function(field, dir){
14028 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14032 * Sort the Records.
14033 * If remote sorting is used, the sort is performed on the server, and the cache is
14034 * reloaded. If local sorting is used, the cache is sorted internally.
14035 * @param {String} fieldName The name of the field to sort by.
14036 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14038 sort : function(fieldName, dir){
14039 var f = this.fields.get(fieldName);
14041 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14043 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14044 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14049 this.sortToggle[f.name] = dir;
14050 this.sortInfo = {field: f.name, direction: dir};
14051 if(!this.remoteSort){
14053 this.fireEvent("datachanged", this);
14055 this.load(this.lastOptions);
14060 * Calls the specified function for each of the Records in the cache.
14061 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14062 * Returning <em>false</em> aborts and exits the iteration.
14063 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14065 each : function(fn, scope){
14066 this.data.each(fn, scope);
14070 * Gets all records modified since the last commit. Modified records are persisted across load operations
14071 * (e.g., during paging).
14072 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14074 getModifiedRecords : function(){
14075 return this.modified;
14079 createFilterFn : function(property, value, anyMatch){
14080 if(!value.exec){ // not a regex
14081 value = String(value);
14082 if(value.length == 0){
14085 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14087 return function(r){
14088 return value.test(r.data[property]);
14093 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14094 * @param {String} property A field on your records
14095 * @param {Number} start The record index to start at (defaults to 0)
14096 * @param {Number} end The last record index to include (defaults to length - 1)
14097 * @return {Number} The sum
14099 sum : function(property, start, end){
14100 var rs = this.data.items, v = 0;
14101 start = start || 0;
14102 end = (end || end === 0) ? end : rs.length-1;
14104 for(var i = start; i <= end; i++){
14105 v += (rs[i].data[property] || 0);
14111 * Filter the records by a specified property.
14112 * @param {String} field A field on your records
14113 * @param {String/RegExp} value Either a string that the field
14114 * should start with or a RegExp to test against the field
14115 * @param {Boolean} anyMatch True to match any part not just the beginning
14117 filter : function(property, value, anyMatch){
14118 var fn = this.createFilterFn(property, value, anyMatch);
14119 return fn ? this.filterBy(fn) : this.clearFilter();
14123 * Filter by a function. The specified function will be called with each
14124 * record in this data source. If the function returns true the record is included,
14125 * otherwise it is filtered.
14126 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14127 * @param {Object} scope (optional) The scope of the function (defaults to this)
14129 filterBy : function(fn, scope){
14130 this.snapshot = this.snapshot || this.data;
14131 this.data = this.queryBy(fn, scope||this);
14132 this.fireEvent("datachanged", this);
14136 * Query the records by a specified property.
14137 * @param {String} field A field on your records
14138 * @param {String/RegExp} value Either a string that the field
14139 * should start with or a RegExp to test against the field
14140 * @param {Boolean} anyMatch True to match any part not just the beginning
14141 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14143 query : function(property, value, anyMatch){
14144 var fn = this.createFilterFn(property, value, anyMatch);
14145 return fn ? this.queryBy(fn) : this.data.clone();
14149 * Query by a function. The specified function will be called with each
14150 * record in this data source. If the function returns true the record is included
14152 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14153 * @param {Object} scope (optional) The scope of the function (defaults to this)
14154 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14156 queryBy : function(fn, scope){
14157 var data = this.snapshot || this.data;
14158 return data.filterBy(fn, scope||this);
14162 * Collects unique values for a particular dataIndex from this store.
14163 * @param {String} dataIndex The property to collect
14164 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14165 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14166 * @return {Array} An array of the unique values
14168 collect : function(dataIndex, allowNull, bypassFilter){
14169 var d = (bypassFilter === true && this.snapshot) ?
14170 this.snapshot.items : this.data.items;
14171 var v, sv, r = [], l = {};
14172 for(var i = 0, len = d.length; i < len; i++){
14173 v = d[i].data[dataIndex];
14175 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14184 * Revert to a view of the Record cache with no filtering applied.
14185 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14187 clearFilter : function(suppressEvent){
14188 if(this.snapshot && this.snapshot != this.data){
14189 this.data = this.snapshot;
14190 delete this.snapshot;
14191 if(suppressEvent !== true){
14192 this.fireEvent("datachanged", this);
14198 afterEdit : function(record){
14199 if(this.modified.indexOf(record) == -1){
14200 this.modified.push(record);
14202 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14206 afterReject : function(record){
14207 this.modified.remove(record);
14208 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14212 afterCommit : function(record){
14213 this.modified.remove(record);
14214 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14218 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14219 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14221 commitChanges : function(){
14222 var m = this.modified.slice(0);
14223 this.modified = [];
14224 for(var i = 0, len = m.length; i < len; i++){
14230 * Cancel outstanding changes on all changed records.
14232 rejectChanges : function(){
14233 var m = this.modified.slice(0);
14234 this.modified = [];
14235 for(var i = 0, len = m.length; i < len; i++){
14240 onMetaChange : function(meta, rtype, o){
14241 this.recordType = rtype;
14242 this.fields = rtype.prototype.fields;
14243 delete this.snapshot;
14244 this.sortInfo = meta.sortInfo || this.sortInfo;
14245 this.modified = [];
14246 this.fireEvent('metachange', this, this.reader.meta);
14249 moveIndex : function(data, type)
14251 var index = this.indexOf(data);
14253 var newIndex = index + type;
14257 this.insert(newIndex, data);
14262 * Ext JS Library 1.1.1
14263 * Copyright(c) 2006-2007, Ext JS, LLC.
14265 * Originally Released Under LGPL - original licence link has changed is not relivant.
14268 * <script type="text/javascript">
14272 * @class Roo.data.SimpleStore
14273 * @extends Roo.data.Store
14274 * Small helper class to make creating Stores from Array data easier.
14275 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14276 * @cfg {Array} fields An array of field definition objects, or field name strings.
14277 * @cfg {Object} an existing reader (eg. copied from another store)
14278 * @cfg {Array} data The multi-dimensional array of data
14280 * @param {Object} config
14282 Roo.data.SimpleStore = function(config)
14284 Roo.data.SimpleStore.superclass.constructor.call(this, {
14286 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14289 Roo.data.Record.create(config.fields)
14291 proxy : new Roo.data.MemoryProxy(config.data)
14295 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14297 * Ext JS Library 1.1.1
14298 * Copyright(c) 2006-2007, Ext JS, LLC.
14300 * Originally Released Under LGPL - original licence link has changed is not relivant.
14303 * <script type="text/javascript">
14308 * @extends Roo.data.Store
14309 * @class Roo.data.JsonStore
14310 * Small helper class to make creating Stores for JSON data easier. <br/>
14312 var store = new Roo.data.JsonStore({
14313 url: 'get-images.php',
14315 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14318 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14319 * JsonReader and HttpProxy (unless inline data is provided).</b>
14320 * @cfg {Array} fields An array of field definition objects, or field name strings.
14322 * @param {Object} config
14324 Roo.data.JsonStore = function(c){
14325 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14326 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14327 reader: new Roo.data.JsonReader(c, c.fields)
14330 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14332 * Ext JS Library 1.1.1
14333 * Copyright(c) 2006-2007, Ext JS, LLC.
14335 * Originally Released Under LGPL - original licence link has changed is not relivant.
14338 * <script type="text/javascript">
14342 Roo.data.Field = function(config){
14343 if(typeof config == "string"){
14344 config = {name: config};
14346 Roo.apply(this, config);
14349 this.type = "auto";
14352 var st = Roo.data.SortTypes;
14353 // named sortTypes are supported, here we look them up
14354 if(typeof this.sortType == "string"){
14355 this.sortType = st[this.sortType];
14358 // set default sortType for strings and dates
14359 if(!this.sortType){
14362 this.sortType = st.asUCString;
14365 this.sortType = st.asDate;
14368 this.sortType = st.none;
14373 var stripRe = /[\$,%]/g;
14375 // prebuilt conversion function for this field, instead of
14376 // switching every time we're reading a value
14378 var cv, dateFormat = this.dateFormat;
14383 cv = function(v){ return v; };
14386 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14390 return v !== undefined && v !== null && v !== '' ?
14391 parseInt(String(v).replace(stripRe, ""), 10) : '';
14396 return v !== undefined && v !== null && v !== '' ?
14397 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14402 cv = function(v){ return v === true || v === "true" || v == 1; };
14409 if(v instanceof Date){
14413 if(dateFormat == "timestamp"){
14414 return new Date(v*1000);
14416 return Date.parseDate(v, dateFormat);
14418 var parsed = Date.parse(v);
14419 return parsed ? new Date(parsed) : null;
14428 Roo.data.Field.prototype = {
14436 * Ext JS Library 1.1.1
14437 * Copyright(c) 2006-2007, Ext JS, LLC.
14439 * Originally Released Under LGPL - original licence link has changed is not relivant.
14442 * <script type="text/javascript">
14445 // Base class for reading structured data from a data source. This class is intended to be
14446 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14449 * @class Roo.data.DataReader
14450 * Base class for reading structured data from a data source. This class is intended to be
14451 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14454 Roo.data.DataReader = function(meta, recordType){
14458 this.recordType = recordType instanceof Array ?
14459 Roo.data.Record.create(recordType) : recordType;
14462 Roo.data.DataReader.prototype = {
14465 readerType : 'Data',
14467 * Create an empty record
14468 * @param {Object} data (optional) - overlay some values
14469 * @return {Roo.data.Record} record created.
14471 newRow : function(d) {
14473 this.recordType.prototype.fields.each(function(c) {
14475 case 'int' : da[c.name] = 0; break;
14476 case 'date' : da[c.name] = new Date(); break;
14477 case 'float' : da[c.name] = 0.0; break;
14478 case 'boolean' : da[c.name] = false; break;
14479 default : da[c.name] = ""; break;
14483 return new this.recordType(Roo.apply(da, d));
14489 * Ext JS Library 1.1.1
14490 * Copyright(c) 2006-2007, Ext JS, LLC.
14492 * Originally Released Under LGPL - original licence link has changed is not relivant.
14495 * <script type="text/javascript">
14499 * @class Roo.data.DataProxy
14500 * @extends Roo.data.Observable
14501 * This class is an abstract base class for implementations which provide retrieval of
14502 * unformatted data objects.<br>
14504 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14505 * (of the appropriate type which knows how to parse the data object) to provide a block of
14506 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14508 * Custom implementations must implement the load method as described in
14509 * {@link Roo.data.HttpProxy#load}.
14511 Roo.data.DataProxy = function(){
14514 * @event beforeload
14515 * Fires before a network request is made to retrieve a data object.
14516 * @param {Object} This DataProxy object.
14517 * @param {Object} params The params parameter to the load function.
14522 * Fires before the load method's callback is called.
14523 * @param {Object} This DataProxy object.
14524 * @param {Object} o The data object.
14525 * @param {Object} arg The callback argument object passed to the load function.
14529 * @event loadexception
14530 * Fires if an Exception occurs during data retrieval.
14531 * @param {Object} This DataProxy object.
14532 * @param {Object} o The data object.
14533 * @param {Object} arg The callback argument object passed to the load function.
14534 * @param {Object} e The Exception.
14536 loadexception : true
14538 Roo.data.DataProxy.superclass.constructor.call(this);
14541 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14544 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14548 * Ext JS Library 1.1.1
14549 * Copyright(c) 2006-2007, Ext JS, LLC.
14551 * Originally Released Under LGPL - original licence link has changed is not relivant.
14554 * <script type="text/javascript">
14557 * @class Roo.data.MemoryProxy
14558 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14559 * to the Reader when its load method is called.
14561 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14563 Roo.data.MemoryProxy = function(data){
14567 Roo.data.MemoryProxy.superclass.constructor.call(this);
14571 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14574 * Load data from the requested source (in this case an in-memory
14575 * data object passed to the constructor), read the data object into
14576 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14577 * process that block using the passed callback.
14578 * @param {Object} params This parameter is not used by the MemoryProxy class.
14579 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14580 * object into a block of Roo.data.Records.
14581 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14582 * The function must be passed <ul>
14583 * <li>The Record block object</li>
14584 * <li>The "arg" argument from the load function</li>
14585 * <li>A boolean success indicator</li>
14587 * @param {Object} scope The scope in which to call the callback
14588 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14590 load : function(params, reader, callback, scope, arg){
14591 params = params || {};
14594 result = reader.readRecords(params.data ? params.data :this.data);
14596 this.fireEvent("loadexception", this, arg, null, e);
14597 callback.call(scope, null, arg, false);
14600 callback.call(scope, result, arg, true);
14604 update : function(params, records){
14609 * Ext JS Library 1.1.1
14610 * Copyright(c) 2006-2007, Ext JS, LLC.
14612 * Originally Released Under LGPL - original licence link has changed is not relivant.
14615 * <script type="text/javascript">
14618 * @class Roo.data.HttpProxy
14619 * @extends Roo.data.DataProxy
14620 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14621 * configured to reference a certain URL.<br><br>
14623 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14624 * from which the running page was served.<br><br>
14626 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14628 * Be aware that to enable the browser to parse an XML document, the server must set
14629 * the Content-Type header in the HTTP response to "text/xml".
14631 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14632 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14633 * will be used to make the request.
14635 Roo.data.HttpProxy = function(conn){
14636 Roo.data.HttpProxy.superclass.constructor.call(this);
14637 // is conn a conn config or a real conn?
14639 this.useAjax = !conn || !conn.events;
14643 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14644 // thse are take from connection...
14647 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14650 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14651 * extra parameters to each request made by this object. (defaults to undefined)
14654 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14655 * to each request made by this object. (defaults to undefined)
14658 * @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)
14661 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14664 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14670 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14674 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14675 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14676 * a finer-grained basis than the DataProxy events.
14678 getConnection : function(){
14679 return this.useAjax ? Roo.Ajax : this.conn;
14683 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14684 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14685 * process that block using the passed callback.
14686 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14687 * for the request to the remote server.
14688 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14689 * object into a block of Roo.data.Records.
14690 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14691 * The function must be passed <ul>
14692 * <li>The Record block object</li>
14693 * <li>The "arg" argument from the load function</li>
14694 * <li>A boolean success indicator</li>
14696 * @param {Object} scope The scope in which to call the callback
14697 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14699 load : function(params, reader, callback, scope, arg){
14700 if(this.fireEvent("beforeload", this, params) !== false){
14702 params : params || {},
14704 callback : callback,
14709 callback : this.loadResponse,
14713 Roo.applyIf(o, this.conn);
14714 if(this.activeRequest){
14715 Roo.Ajax.abort(this.activeRequest);
14717 this.activeRequest = Roo.Ajax.request(o);
14719 this.conn.request(o);
14722 callback.call(scope||this, null, arg, false);
14727 loadResponse : function(o, success, response){
14728 delete this.activeRequest;
14730 this.fireEvent("loadexception", this, o, response);
14731 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14736 result = o.reader.read(response);
14738 this.fireEvent("loadexception", this, o, response, e);
14739 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14743 this.fireEvent("load", this, o, o.request.arg);
14744 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14748 update : function(dataSet){
14753 updateResponse : function(dataSet){
14758 * Ext JS Library 1.1.1
14759 * Copyright(c) 2006-2007, Ext JS, LLC.
14761 * Originally Released Under LGPL - original licence link has changed is not relivant.
14764 * <script type="text/javascript">
14768 * @class Roo.data.ScriptTagProxy
14769 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14770 * other than the originating domain of the running page.<br><br>
14772 * <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
14773 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14775 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14776 * source code that is used as the source inside a <script> tag.<br><br>
14778 * In order for the browser to process the returned data, the server must wrap the data object
14779 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14780 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14781 * depending on whether the callback name was passed:
14784 boolean scriptTag = false;
14785 String cb = request.getParameter("callback");
14788 response.setContentType("text/javascript");
14790 response.setContentType("application/x-json");
14792 Writer out = response.getWriter();
14794 out.write(cb + "(");
14796 out.print(dataBlock.toJsonString());
14803 * @param {Object} config A configuration object.
14805 Roo.data.ScriptTagProxy = function(config){
14806 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14807 Roo.apply(this, config);
14808 this.head = document.getElementsByTagName("head")[0];
14811 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14813 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14815 * @cfg {String} url The URL from which to request the data object.
14818 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14822 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14823 * the server the name of the callback function set up by the load call to process the returned data object.
14824 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14825 * javascript output which calls this named function passing the data object as its only parameter.
14827 callbackParam : "callback",
14829 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14830 * name to the request.
14835 * Load data from the configured URL, read the data object into
14836 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14837 * process that block using the passed callback.
14838 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14839 * for the request to the remote server.
14840 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14841 * object into a block of Roo.data.Records.
14842 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14843 * The function must be passed <ul>
14844 * <li>The Record block object</li>
14845 * <li>The "arg" argument from the load function</li>
14846 * <li>A boolean success indicator</li>
14848 * @param {Object} scope The scope in which to call the callback
14849 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14851 load : function(params, reader, callback, scope, arg){
14852 if(this.fireEvent("beforeload", this, params) !== false){
14854 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14856 var url = this.url;
14857 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14859 url += "&_dc=" + (new Date().getTime());
14861 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14864 cb : "stcCallback"+transId,
14865 scriptId : "stcScript"+transId,
14869 callback : callback,
14875 window[trans.cb] = function(o){
14876 conn.handleResponse(o, trans);
14879 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14881 if(this.autoAbort !== false){
14885 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14887 var script = document.createElement("script");
14888 script.setAttribute("src", url);
14889 script.setAttribute("type", "text/javascript");
14890 script.setAttribute("id", trans.scriptId);
14891 this.head.appendChild(script);
14893 this.trans = trans;
14895 callback.call(scope||this, null, arg, false);
14900 isLoading : function(){
14901 return this.trans ? true : false;
14905 * Abort the current server request.
14907 abort : function(){
14908 if(this.isLoading()){
14909 this.destroyTrans(this.trans);
14914 destroyTrans : function(trans, isLoaded){
14915 this.head.removeChild(document.getElementById(trans.scriptId));
14916 clearTimeout(trans.timeoutId);
14918 window[trans.cb] = undefined;
14920 delete window[trans.cb];
14923 // if hasn't been loaded, wait for load to remove it to prevent script error
14924 window[trans.cb] = function(){
14925 window[trans.cb] = undefined;
14927 delete window[trans.cb];
14934 handleResponse : function(o, trans){
14935 this.trans = false;
14936 this.destroyTrans(trans, true);
14939 result = trans.reader.readRecords(o);
14941 this.fireEvent("loadexception", this, o, trans.arg, e);
14942 trans.callback.call(trans.scope||window, null, trans.arg, false);
14945 this.fireEvent("load", this, o, trans.arg);
14946 trans.callback.call(trans.scope||window, result, trans.arg, true);
14950 handleFailure : function(trans){
14951 this.trans = false;
14952 this.destroyTrans(trans, false);
14953 this.fireEvent("loadexception", this, null, trans.arg);
14954 trans.callback.call(trans.scope||window, null, trans.arg, false);
14958 * Ext JS Library 1.1.1
14959 * Copyright(c) 2006-2007, Ext JS, LLC.
14961 * Originally Released Under LGPL - original licence link has changed is not relivant.
14964 * <script type="text/javascript">
14968 * @class Roo.data.JsonReader
14969 * @extends Roo.data.DataReader
14970 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14971 * based on mappings in a provided Roo.data.Record constructor.
14973 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14974 * in the reply previously.
14979 var RecordDef = Roo.data.Record.create([
14980 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14981 {name: 'occupation'} // This field will use "occupation" as the mapping.
14983 var myReader = new Roo.data.JsonReader({
14984 totalProperty: "results", // The property which contains the total dataset size (optional)
14985 root: "rows", // The property which contains an Array of row objects
14986 id: "id" // The property within each row object that provides an ID for the record (optional)
14990 * This would consume a JSON file like this:
14992 { 'results': 2, 'rows': [
14993 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14994 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14997 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14998 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14999 * paged from the remote server.
15000 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15001 * @cfg {String} root name of the property which contains the Array of row objects.
15002 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15003 * @cfg {Array} fields Array of field definition objects
15005 * Create a new JsonReader
15006 * @param {Object} meta Metadata configuration options
15007 * @param {Object} recordType Either an Array of field definition objects,
15008 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15010 Roo.data.JsonReader = function(meta, recordType){
15013 // set some defaults:
15014 Roo.applyIf(meta, {
15015 totalProperty: 'total',
15016 successProperty : 'success',
15021 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15023 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15025 readerType : 'Json',
15028 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15029 * Used by Store query builder to append _requestMeta to params.
15032 metaFromRemote : false,
15034 * This method is only used by a DataProxy which has retrieved data from a remote server.
15035 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15036 * @return {Object} data A data block which is used by an Roo.data.Store object as
15037 * a cache of Roo.data.Records.
15039 read : function(response){
15040 var json = response.responseText;
15042 var o = /* eval:var:o */ eval("("+json+")");
15044 throw {message: "JsonReader.read: Json object not found"};
15050 this.metaFromRemote = true;
15051 this.meta = o.metaData;
15052 this.recordType = Roo.data.Record.create(o.metaData.fields);
15053 this.onMetaChange(this.meta, this.recordType, o);
15055 return this.readRecords(o);
15058 // private function a store will implement
15059 onMetaChange : function(meta, recordType, o){
15066 simpleAccess: function(obj, subsc) {
15073 getJsonAccessor: function(){
15075 return function(expr) {
15077 return(re.test(expr))
15078 ? new Function("obj", "return obj." + expr)
15083 return Roo.emptyFn;
15088 * Create a data block containing Roo.data.Records from an XML document.
15089 * @param {Object} o An object which contains an Array of row objects in the property specified
15090 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15091 * which contains the total size of the dataset.
15092 * @return {Object} data A data block which is used by an Roo.data.Store object as
15093 * a cache of Roo.data.Records.
15095 readRecords : function(o){
15097 * After any data loads, the raw JSON data is available for further custom processing.
15101 var s = this.meta, Record = this.recordType,
15102 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15104 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15106 if(s.totalProperty) {
15107 this.getTotal = this.getJsonAccessor(s.totalProperty);
15109 if(s.successProperty) {
15110 this.getSuccess = this.getJsonAccessor(s.successProperty);
15112 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15114 var g = this.getJsonAccessor(s.id);
15115 this.getId = function(rec) {
15117 return (r === undefined || r === "") ? null : r;
15120 this.getId = function(){return null;};
15123 for(var jj = 0; jj < fl; jj++){
15125 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15126 this.ef[jj] = this.getJsonAccessor(map);
15130 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15131 if(s.totalProperty){
15132 var vt = parseInt(this.getTotal(o), 10);
15137 if(s.successProperty){
15138 var vs = this.getSuccess(o);
15139 if(vs === false || vs === 'false'){
15144 for(var i = 0; i < c; i++){
15147 var id = this.getId(n);
15148 for(var j = 0; j < fl; j++){
15150 var v = this.ef[j](n);
15152 Roo.log('missing convert for ' + f.name);
15156 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15158 var record = new Record(values, id);
15160 records[i] = record;
15166 totalRecords : totalRecords
15169 // used when loading children.. @see loadDataFromChildren
15170 toLoadData: function(rec)
15172 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15173 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15174 return { data : data, total : data.length };
15179 * Ext JS Library 1.1.1
15180 * Copyright(c) 2006-2007, Ext JS, LLC.
15182 * Originally Released Under LGPL - original licence link has changed is not relivant.
15185 * <script type="text/javascript">
15189 * @class Roo.data.ArrayReader
15190 * @extends Roo.data.DataReader
15191 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15192 * Each element of that Array represents a row of data fields. The
15193 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15194 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15198 var RecordDef = Roo.data.Record.create([
15199 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15200 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15202 var myReader = new Roo.data.ArrayReader({
15203 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15207 * This would consume an Array like this:
15209 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15213 * Create a new JsonReader
15214 * @param {Object} meta Metadata configuration options.
15215 * @param {Object|Array} recordType Either an Array of field definition objects
15217 * @cfg {Array} fields Array of field definition objects
15218 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15219 * as specified to {@link Roo.data.Record#create},
15220 * or an {@link Roo.data.Record} object
15223 * created using {@link Roo.data.Record#create}.
15225 Roo.data.ArrayReader = function(meta, recordType)
15227 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15230 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15233 * Create a data block containing Roo.data.Records from an XML document.
15234 * @param {Object} o An Array of row objects which represents the dataset.
15235 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15236 * a cache of Roo.data.Records.
15238 readRecords : function(o)
15240 var sid = this.meta ? this.meta.id : null;
15241 var recordType = this.recordType, fields = recordType.prototype.fields;
15244 for(var i = 0; i < root.length; i++){
15247 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15248 for(var j = 0, jlen = fields.length; j < jlen; j++){
15249 var f = fields.items[j];
15250 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15251 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15253 values[f.name] = v;
15255 var record = new recordType(values, id);
15257 records[records.length] = record;
15261 totalRecords : records.length
15264 // used when loading children.. @see loadDataFromChildren
15265 toLoadData: function(rec)
15267 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15268 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15279 * @class Roo.bootstrap.ComboBox
15280 * @extends Roo.bootstrap.TriggerField
15281 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15282 * @cfg {Boolean} append (true|false) default false
15283 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15284 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15285 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15286 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15287 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15288 * @cfg {Boolean} animate default true
15289 * @cfg {Boolean} emptyResultText only for touch device
15290 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15291 * @cfg {String} emptyTitle default ''
15292 * @cfg {Number} width fixed with? experimental
15294 * Create a new ComboBox.
15295 * @param {Object} config Configuration options
15297 Roo.bootstrap.ComboBox = function(config){
15298 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15302 * Fires when the dropdown list is expanded
15303 * @param {Roo.bootstrap.ComboBox} combo This combo box
15308 * Fires when the dropdown list is collapsed
15309 * @param {Roo.bootstrap.ComboBox} combo This combo box
15313 * @event beforeselect
15314 * Fires before a list item is selected. Return false to cancel the selection.
15315 * @param {Roo.bootstrap.ComboBox} combo This combo box
15316 * @param {Roo.data.Record} record The data record returned from the underlying store
15317 * @param {Number} index The index of the selected item in the dropdown list
15319 'beforeselect' : true,
15322 * Fires when a list item is selected
15323 * @param {Roo.bootstrap.ComboBox} combo This combo box
15324 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15325 * @param {Number} index The index of the selected item in the dropdown list
15329 * @event beforequery
15330 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15331 * The event object passed has these properties:
15332 * @param {Roo.bootstrap.ComboBox} combo This combo box
15333 * @param {String} query The query
15334 * @param {Boolean} forceAll true to force "all" query
15335 * @param {Boolean} cancel true to cancel the query
15336 * @param {Object} e The query event object
15338 'beforequery': true,
15341 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15342 * @param {Roo.bootstrap.ComboBox} combo This combo box
15347 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15348 * @param {Roo.bootstrap.ComboBox} combo This combo box
15349 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15354 * Fires when the remove value from the combobox array
15355 * @param {Roo.bootstrap.ComboBox} combo This combo box
15359 * @event afterremove
15360 * Fires when the remove value from the combobox array
15361 * @param {Roo.bootstrap.ComboBox} combo This combo box
15363 'afterremove' : true,
15365 * @event specialfilter
15366 * Fires when specialfilter
15367 * @param {Roo.bootstrap.ComboBox} combo This combo box
15369 'specialfilter' : true,
15372 * Fires when tick the element
15373 * @param {Roo.bootstrap.ComboBox} combo This combo box
15377 * @event touchviewdisplay
15378 * Fires when touch view require special display (default is using displayField)
15379 * @param {Roo.bootstrap.ComboBox} combo This combo box
15380 * @param {Object} cfg set html .
15382 'touchviewdisplay' : true
15387 this.tickItems = [];
15389 this.selectedIndex = -1;
15390 if(this.mode == 'local'){
15391 if(config.queryDelay === undefined){
15392 this.queryDelay = 10;
15394 if(config.minChars === undefined){
15400 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15403 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15404 * rendering into an Roo.Editor, defaults to false)
15407 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15408 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15411 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15414 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15415 * the dropdown list (defaults to undefined, with no header element)
15419 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15423 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15425 listWidth: undefined,
15427 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15428 * mode = 'remote' or 'text' if mode = 'local')
15430 displayField: undefined,
15433 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15434 * mode = 'remote' or 'value' if mode = 'local').
15435 * Note: use of a valueField requires the user make a selection
15436 * in order for a value to be mapped.
15438 valueField: undefined,
15440 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15445 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15446 * field's data value (defaults to the underlying DOM element's name)
15448 hiddenName: undefined,
15450 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15454 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15456 selectedClass: 'active',
15459 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15463 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15464 * anchor positions (defaults to 'tl-bl')
15466 listAlign: 'tl-bl?',
15468 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15472 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15473 * query specified by the allQuery config option (defaults to 'query')
15475 triggerAction: 'query',
15477 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15478 * (defaults to 4, does not apply if editable = false)
15482 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15483 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15487 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15488 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15492 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15493 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15497 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15498 * when editable = true (defaults to false)
15500 selectOnFocus:false,
15502 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15504 queryParam: 'query',
15506 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15507 * when mode = 'remote' (defaults to 'Loading...')
15509 loadingText: 'Loading...',
15511 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15515 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15519 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15520 * traditional select (defaults to true)
15524 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15528 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15532 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15533 * listWidth has a higher value)
15537 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15538 * allow the user to set arbitrary text into the field (defaults to false)
15540 forceSelection:false,
15542 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15543 * if typeAhead = true (defaults to 250)
15545 typeAheadDelay : 250,
15547 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15548 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15550 valueNotFoundText : undefined,
15552 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15554 blockFocus : false,
15557 * @cfg {Boolean} disableClear Disable showing of clear button.
15559 disableClear : false,
15561 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15563 alwaysQuery : false,
15566 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15571 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15573 invalidClass : "has-warning",
15576 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15578 validClass : "has-success",
15581 * @cfg {Boolean} specialFilter (true|false) special filter default false
15583 specialFilter : false,
15586 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15588 mobileTouchView : true,
15591 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15593 useNativeIOS : false,
15596 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15598 mobile_restrict_height : false,
15600 ios_options : false,
15612 btnPosition : 'right',
15613 triggerList : true,
15614 showToggleBtn : true,
15616 emptyResultText: 'Empty',
15617 triggerText : 'Select',
15621 // element that contains real text value.. (when hidden is used..)
15623 getAutoCreate : function()
15628 * Render classic select for iso
15631 if(Roo.isIOS && this.useNativeIOS){
15632 cfg = this.getAutoCreateNativeIOS();
15640 if(Roo.isTouch && this.mobileTouchView){
15641 cfg = this.getAutoCreateTouchView();
15648 if(!this.tickable){
15649 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15654 * ComboBox with tickable selections
15657 var align = this.labelAlign || this.parentLabelAlign();
15660 cls : 'form-group roo-combobox-tickable' //input-group
15663 var btn_text_select = '';
15664 var btn_text_done = '';
15665 var btn_text_cancel = '';
15667 if (this.btn_text_show) {
15668 btn_text_select = 'Select';
15669 btn_text_done = 'Done';
15670 btn_text_cancel = 'Cancel';
15675 cls : 'tickable-buttons',
15680 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15681 //html : this.triggerText
15682 html: btn_text_select
15688 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15690 html: btn_text_done
15696 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15698 html: btn_text_cancel
15704 buttons.cn.unshift({
15706 cls: 'roo-select2-search-field-input'
15712 Roo.each(buttons.cn, function(c){
15714 c.cls += ' btn-' + _this.size;
15717 if (_this.disabled) {
15724 style : 'display: contents',
15729 cls: 'form-hidden-field'
15733 cls: 'roo-select2-choices',
15737 cls: 'roo-select2-search-field',
15748 cls: 'roo-select2-container input-group roo-select2-container-multi',
15754 // cls: 'typeahead typeahead-long dropdown-menu',
15755 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15760 if(this.hasFeedback && !this.allowBlank){
15764 cls: 'glyphicon form-control-feedback'
15767 combobox.cn.push(feedback);
15774 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15775 tooltip : 'This field is required'
15777 if (Roo.bootstrap.version == 4) {
15780 style : 'display:none'
15783 if (align ==='left' && this.fieldLabel.length) {
15785 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15792 cls : 'control-label col-form-label',
15793 html : this.fieldLabel
15805 var labelCfg = cfg.cn[1];
15806 var contentCfg = cfg.cn[2];
15809 if(this.indicatorpos == 'right'){
15815 cls : 'control-label col-form-label',
15819 html : this.fieldLabel
15835 labelCfg = cfg.cn[0];
15836 contentCfg = cfg.cn[1];
15840 if(this.labelWidth > 12){
15841 labelCfg.style = "width: " + this.labelWidth + 'px';
15843 if(this.width * 1 > 0){
15844 contentCfg.style = "width: " + this.width + 'px';
15846 if(this.labelWidth < 13 && this.labelmd == 0){
15847 this.labelmd = this.labelWidth;
15850 if(this.labellg > 0){
15851 labelCfg.cls += ' col-lg-' + this.labellg;
15852 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15855 if(this.labelmd > 0){
15856 labelCfg.cls += ' col-md-' + this.labelmd;
15857 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15860 if(this.labelsm > 0){
15861 labelCfg.cls += ' col-sm-' + this.labelsm;
15862 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15865 if(this.labelxs > 0){
15866 labelCfg.cls += ' col-xs-' + this.labelxs;
15867 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15871 } else if ( this.fieldLabel.length) {
15872 // Roo.log(" label");
15877 //cls : 'input-group-addon',
15878 html : this.fieldLabel
15883 if(this.indicatorpos == 'right'){
15887 //cls : 'input-group-addon',
15888 html : this.fieldLabel
15898 // Roo.log(" no label && no align");
15905 ['xs','sm','md','lg'].map(function(size){
15906 if (settings[size]) {
15907 cfg.cls += ' col-' + size + '-' + settings[size];
15915 _initEventsCalled : false,
15918 initEvents: function()
15920 if (this._initEventsCalled) { // as we call render... prevent looping...
15923 this._initEventsCalled = true;
15926 throw "can not find store for combo";
15929 this.indicator = this.indicatorEl();
15931 this.store = Roo.factory(this.store, Roo.data);
15932 this.store.parent = this;
15934 // if we are building from html. then this element is so complex, that we can not really
15935 // use the rendered HTML.
15936 // so we have to trash and replace the previous code.
15937 if (Roo.XComponent.build_from_html) {
15938 // remove this element....
15939 var e = this.el.dom, k=0;
15940 while (e ) { e = e.previousSibling; ++k;}
15945 this.rendered = false;
15947 this.render(this.parent().getChildContainer(true), k);
15950 if(Roo.isIOS && this.useNativeIOS){
15951 this.initIOSView();
15959 if(Roo.isTouch && this.mobileTouchView){
15960 this.initTouchView();
15965 this.initTickableEvents();
15969 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15971 if(this.hiddenName){
15973 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15975 this.hiddenField.dom.value =
15976 this.hiddenValue !== undefined ? this.hiddenValue :
15977 this.value !== undefined ? this.value : '';
15979 // prevent input submission
15980 this.el.dom.removeAttribute('name');
15981 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15986 // this.el.dom.setAttribute('autocomplete', 'off');
15989 var cls = 'x-combo-list';
15991 //this.list = new Roo.Layer({
15992 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15998 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15999 _this.list.setWidth(lw);
16002 this.list.on('mouseover', this.onViewOver, this);
16003 this.list.on('mousemove', this.onViewMove, this);
16004 this.list.on('scroll', this.onViewScroll, this);
16007 this.list.swallowEvent('mousewheel');
16008 this.assetHeight = 0;
16011 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16012 this.assetHeight += this.header.getHeight();
16015 this.innerList = this.list.createChild({cls:cls+'-inner'});
16016 this.innerList.on('mouseover', this.onViewOver, this);
16017 this.innerList.on('mousemove', this.onViewMove, this);
16018 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16020 if(this.allowBlank && !this.pageSize && !this.disableClear){
16021 this.footer = this.list.createChild({cls:cls+'-ft'});
16022 this.pageTb = new Roo.Toolbar(this.footer);
16026 this.footer = this.list.createChild({cls:cls+'-ft'});
16027 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16028 {pageSize: this.pageSize});
16032 if (this.pageTb && this.allowBlank && !this.disableClear) {
16034 this.pageTb.add(new Roo.Toolbar.Fill(), {
16035 cls: 'x-btn-icon x-btn-clear',
16037 handler: function()
16040 _this.clearValue();
16041 _this.onSelect(false, -1);
16046 this.assetHeight += this.footer.getHeight();
16051 this.tpl = Roo.bootstrap.version == 4 ?
16052 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16053 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16056 this.view = new Roo.View(this.list, this.tpl, {
16057 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16059 //this.view.wrapEl.setDisplayed(false);
16060 this.view.on('click', this.onViewClick, this);
16063 this.store.on('beforeload', this.onBeforeLoad, this);
16064 this.store.on('load', this.onLoad, this);
16065 this.store.on('loadexception', this.onLoadException, this);
16067 if(this.resizable){
16068 this.resizer = new Roo.Resizable(this.list, {
16069 pinned:true, handles:'se'
16071 this.resizer.on('resize', function(r, w, h){
16072 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16073 this.listWidth = w;
16074 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16075 this.restrictHeight();
16077 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16080 if(!this.editable){
16081 this.editable = true;
16082 this.setEditable(false);
16087 if (typeof(this.events.add.listeners) != 'undefined') {
16089 this.addicon = this.wrap.createChild(
16090 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16092 this.addicon.on('click', function(e) {
16093 this.fireEvent('add', this);
16096 if (typeof(this.events.edit.listeners) != 'undefined') {
16098 this.editicon = this.wrap.createChild(
16099 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16100 if (this.addicon) {
16101 this.editicon.setStyle('margin-left', '40px');
16103 this.editicon.on('click', function(e) {
16105 // we fire even if inothing is selected..
16106 this.fireEvent('edit', this, this.lastData );
16112 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16113 "up" : function(e){
16114 this.inKeyMode = true;
16118 "down" : function(e){
16119 if(!this.isExpanded()){
16120 this.onTriggerClick();
16122 this.inKeyMode = true;
16127 "enter" : function(e){
16128 // this.onViewClick();
16132 if(this.fireEvent("specialkey", this, e)){
16133 this.onViewClick(false);
16139 "esc" : function(e){
16143 "tab" : function(e){
16146 if(this.fireEvent("specialkey", this, e)){
16147 this.onViewClick(false);
16155 doRelay : function(foo, bar, hname){
16156 if(hname == 'down' || this.scope.isExpanded()){
16157 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16166 this.queryDelay = Math.max(this.queryDelay || 10,
16167 this.mode == 'local' ? 10 : 250);
16170 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16172 if(this.typeAhead){
16173 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16175 if(this.editable !== false){
16176 this.inputEl().on("keyup", this.onKeyUp, this);
16178 if(this.forceSelection){
16179 this.inputEl().on('blur', this.doForce, this);
16183 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16184 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16188 initTickableEvents: function()
16192 if(this.hiddenName){
16194 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16196 this.hiddenField.dom.value =
16197 this.hiddenValue !== undefined ? this.hiddenValue :
16198 this.value !== undefined ? this.value : '';
16200 // prevent input submission
16201 this.el.dom.removeAttribute('name');
16202 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16207 // this.list = this.el.select('ul.dropdown-menu',true).first();
16209 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16210 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16211 if(this.triggerList){
16212 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16215 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16216 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16218 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16219 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16221 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16222 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16224 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16225 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16226 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16229 this.cancelBtn.hide();
16234 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16235 _this.list.setWidth(lw);
16238 this.list.on('mouseover', this.onViewOver, this);
16239 this.list.on('mousemove', this.onViewMove, this);
16241 this.list.on('scroll', this.onViewScroll, this);
16244 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16245 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16248 this.view = new Roo.View(this.list, this.tpl, {
16253 selectedClass: this.selectedClass
16256 //this.view.wrapEl.setDisplayed(false);
16257 this.view.on('click', this.onViewClick, this);
16261 this.store.on('beforeload', this.onBeforeLoad, this);
16262 this.store.on('load', this.onLoad, this);
16263 this.store.on('loadexception', this.onLoadException, this);
16266 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16267 "up" : function(e){
16268 this.inKeyMode = true;
16272 "down" : function(e){
16273 this.inKeyMode = true;
16277 "enter" : function(e){
16278 if(this.fireEvent("specialkey", this, e)){
16279 this.onViewClick(false);
16285 "esc" : function(e){
16286 this.onTickableFooterButtonClick(e, false, false);
16289 "tab" : function(e){
16290 this.fireEvent("specialkey", this, e);
16292 this.onTickableFooterButtonClick(e, false, false);
16299 doRelay : function(e, fn, key){
16300 if(this.scope.isExpanded()){
16301 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16310 this.queryDelay = Math.max(this.queryDelay || 10,
16311 this.mode == 'local' ? 10 : 250);
16314 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16316 if(this.typeAhead){
16317 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16320 if(this.editable !== false){
16321 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16324 this.indicator = this.indicatorEl();
16326 if(this.indicator){
16327 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16328 this.indicator.hide();
16333 onDestroy : function(){
16335 this.view.setStore(null);
16336 this.view.el.removeAllListeners();
16337 this.view.el.remove();
16338 this.view.purgeListeners();
16341 this.list.dom.innerHTML = '';
16345 this.store.un('beforeload', this.onBeforeLoad, this);
16346 this.store.un('load', this.onLoad, this);
16347 this.store.un('loadexception', this.onLoadException, this);
16349 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16353 fireKey : function(e){
16354 if(e.isNavKeyPress() && !this.list.isVisible()){
16355 this.fireEvent("specialkey", this, e);
16360 onResize: function(w, h)
16364 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16366 // if(typeof w != 'number'){
16367 // // we do not handle it!?!?
16370 // var tw = this.trigger.getWidth();
16371 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16372 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16374 // this.inputEl().setWidth( this.adjustWidth('input', x));
16376 // //this.trigger.setStyle('left', x+'px');
16378 // if(this.list && this.listWidth === undefined){
16379 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16380 // this.list.setWidth(lw);
16381 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16389 * Allow or prevent the user from directly editing the field text. If false is passed,
16390 * the user will only be able to select from the items defined in the dropdown list. This method
16391 * is the runtime equivalent of setting the 'editable' config option at config time.
16392 * @param {Boolean} value True to allow the user to directly edit the field text
16394 setEditable : function(value){
16395 if(value == this.editable){
16398 this.editable = value;
16400 this.inputEl().dom.setAttribute('readOnly', true);
16401 this.inputEl().on('mousedown', this.onTriggerClick, this);
16402 this.inputEl().addClass('x-combo-noedit');
16404 this.inputEl().dom.removeAttribute('readOnly');
16405 this.inputEl().un('mousedown', this.onTriggerClick, this);
16406 this.inputEl().removeClass('x-combo-noedit');
16412 onBeforeLoad : function(combo,opts){
16413 if(!this.hasFocus){
16417 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16419 this.restrictHeight();
16420 this.selectedIndex = -1;
16424 onLoad : function(){
16426 this.hasQuery = false;
16428 if(!this.hasFocus){
16432 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16433 this.loading.hide();
16436 if(this.store.getCount() > 0){
16439 this.restrictHeight();
16440 if(this.lastQuery == this.allQuery){
16441 if(this.editable && !this.tickable){
16442 this.inputEl().dom.select();
16446 !this.selectByValue(this.value, true) &&
16449 !this.store.lastOptions ||
16450 typeof(this.store.lastOptions.add) == 'undefined' ||
16451 this.store.lastOptions.add != true
16454 this.select(0, true);
16457 if(this.autoFocus){
16460 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16461 this.taTask.delay(this.typeAheadDelay);
16465 this.onEmptyResults();
16471 onLoadException : function()
16473 this.hasQuery = false;
16475 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16476 this.loading.hide();
16479 if(this.tickable && this.editable){
16484 // only causes errors at present
16485 //Roo.log(this.store.reader.jsonData);
16486 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16488 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16494 onTypeAhead : function(){
16495 if(this.store.getCount() > 0){
16496 var r = this.store.getAt(0);
16497 var newValue = r.data[this.displayField];
16498 var len = newValue.length;
16499 var selStart = this.getRawValue().length;
16501 if(selStart != len){
16502 this.setRawValue(newValue);
16503 this.selectText(selStart, newValue.length);
16509 onSelect : function(record, index){
16511 if(this.fireEvent('beforeselect', this, record, index) !== false){
16513 this.setFromData(index > -1 ? record.data : false);
16516 this.fireEvent('select', this, record, index);
16521 * Returns the currently selected field value or empty string if no value is set.
16522 * @return {String} value The selected value
16524 getValue : function()
16526 if(Roo.isIOS && this.useNativeIOS){
16527 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16531 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16534 if(this.valueField){
16535 return typeof this.value != 'undefined' ? this.value : '';
16537 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16541 getRawValue : function()
16543 if(Roo.isIOS && this.useNativeIOS){
16544 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16547 var v = this.inputEl().getValue();
16553 * Clears any text/value currently set in the field
16555 clearValue : function(){
16557 if(this.hiddenField){
16558 this.hiddenField.dom.value = '';
16561 this.setRawValue('');
16562 this.lastSelectionText = '';
16563 this.lastData = false;
16565 var close = this.closeTriggerEl();
16576 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16577 * will be displayed in the field. If the value does not match the data value of an existing item,
16578 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16579 * Otherwise the field will be blank (although the value will still be set).
16580 * @param {String} value The value to match
16582 setValue : function(v)
16584 if(Roo.isIOS && this.useNativeIOS){
16585 this.setIOSValue(v);
16595 if(this.valueField){
16596 var r = this.findRecord(this.valueField, v);
16598 text = r.data[this.displayField];
16599 }else if(this.valueNotFoundText !== undefined){
16600 text = this.valueNotFoundText;
16603 this.lastSelectionText = text;
16604 if(this.hiddenField){
16605 this.hiddenField.dom.value = v;
16607 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16610 var close = this.closeTriggerEl();
16613 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16619 * @property {Object} the last set data for the element
16624 * Sets the value of the field based on a object which is related to the record format for the store.
16625 * @param {Object} value the value to set as. or false on reset?
16627 setFromData : function(o){
16634 var dv = ''; // display value
16635 var vv = ''; // value value..
16637 if (this.displayField) {
16638 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16640 // this is an error condition!!!
16641 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16644 if(this.valueField){
16645 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16648 var close = this.closeTriggerEl();
16651 if(dv.length || vv * 1 > 0){
16653 this.blockFocus=true;
16659 if(this.hiddenField){
16660 this.hiddenField.dom.value = vv;
16662 this.lastSelectionText = dv;
16663 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16667 // no hidden field.. - we store the value in 'value', but still display
16668 // display field!!!!
16669 this.lastSelectionText = dv;
16670 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16677 reset : function(){
16678 // overridden so that last data is reset..
16685 this.setValue(this.originalValue);
16686 //this.clearInvalid();
16687 this.lastData = false;
16689 this.view.clearSelections();
16695 findRecord : function(prop, value){
16697 if(this.store.getCount() > 0){
16698 this.store.each(function(r){
16699 if(r.data[prop] == value){
16709 getName: function()
16711 // returns hidden if it's set..
16712 if (!this.rendered) {return ''};
16713 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16717 onViewMove : function(e, t){
16718 this.inKeyMode = false;
16722 onViewOver : function(e, t){
16723 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16726 var item = this.view.findItemFromChild(t);
16729 var index = this.view.indexOf(item);
16730 this.select(index, false);
16735 onViewClick : function(view, doFocus, el, e)
16737 var index = this.view.getSelectedIndexes()[0];
16739 var r = this.store.getAt(index);
16743 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16750 Roo.each(this.tickItems, function(v,k){
16752 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16754 _this.tickItems.splice(k, 1);
16756 if(typeof(e) == 'undefined' && view == false){
16757 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16769 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16770 this.tickItems.push(r.data);
16773 if(typeof(e) == 'undefined' && view == false){
16774 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16781 this.onSelect(r, index);
16783 if(doFocus !== false && !this.blockFocus){
16784 this.inputEl().focus();
16789 restrictHeight : function(){
16790 //this.innerList.dom.style.height = '';
16791 //var inner = this.innerList.dom;
16792 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16793 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16794 //this.list.beginUpdate();
16795 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16796 this.list.alignTo(this.inputEl(), this.listAlign);
16797 this.list.alignTo(this.inputEl(), this.listAlign);
16798 //this.list.endUpdate();
16802 onEmptyResults : function(){
16804 if(this.tickable && this.editable){
16805 this.hasFocus = false;
16806 this.restrictHeight();
16814 * Returns true if the dropdown list is expanded, else false.
16816 isExpanded : function(){
16817 return this.list.isVisible();
16821 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16822 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16823 * @param {String} value The data value of the item to select
16824 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16825 * selected item if it is not currently in view (defaults to true)
16826 * @return {Boolean} True if the value matched an item in the list, else false
16828 selectByValue : function(v, scrollIntoView){
16829 if(v !== undefined && v !== null){
16830 var r = this.findRecord(this.valueField || this.displayField, v);
16832 this.select(this.store.indexOf(r), scrollIntoView);
16840 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16841 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16842 * @param {Number} index The zero-based index of the list item to select
16843 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16844 * selected item if it is not currently in view (defaults to true)
16846 select : function(index, scrollIntoView){
16847 this.selectedIndex = index;
16848 this.view.select(index);
16849 if(scrollIntoView !== false){
16850 var el = this.view.getNode(index);
16852 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16855 this.list.scrollChildIntoView(el, false);
16861 selectNext : function(){
16862 var ct = this.store.getCount();
16864 if(this.selectedIndex == -1){
16866 }else if(this.selectedIndex < ct-1){
16867 this.select(this.selectedIndex+1);
16873 selectPrev : function(){
16874 var ct = this.store.getCount();
16876 if(this.selectedIndex == -1){
16878 }else if(this.selectedIndex != 0){
16879 this.select(this.selectedIndex-1);
16885 onKeyUp : function(e){
16886 if(this.editable !== false && !e.isSpecialKey()){
16887 this.lastKey = e.getKey();
16888 this.dqTask.delay(this.queryDelay);
16893 validateBlur : function(){
16894 return !this.list || !this.list.isVisible();
16898 initQuery : function(){
16900 var v = this.getRawValue();
16902 if(this.tickable && this.editable){
16903 v = this.tickableInputEl().getValue();
16910 doForce : function(){
16911 if(this.inputEl().dom.value.length > 0){
16912 this.inputEl().dom.value =
16913 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16919 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16920 * query allowing the query action to be canceled if needed.
16921 * @param {String} query The SQL query to execute
16922 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16923 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16924 * saved in the current store (defaults to false)
16926 doQuery : function(q, forceAll){
16928 if(q === undefined || q === null){
16933 forceAll: forceAll,
16937 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16942 forceAll = qe.forceAll;
16943 if(forceAll === true || (q.length >= this.minChars)){
16945 this.hasQuery = true;
16947 if(this.lastQuery != q || this.alwaysQuery){
16948 this.lastQuery = q;
16949 if(this.mode == 'local'){
16950 this.selectedIndex = -1;
16952 this.store.clearFilter();
16955 if(this.specialFilter){
16956 this.fireEvent('specialfilter', this);
16961 this.store.filter(this.displayField, q);
16964 this.store.fireEvent("datachanged", this.store);
16971 this.store.baseParams[this.queryParam] = q;
16973 var options = {params : this.getParams(q)};
16976 options.add = true;
16977 options.params.start = this.page * this.pageSize;
16980 this.store.load(options);
16983 * this code will make the page width larger, at the beginning, the list not align correctly,
16984 * we should expand the list on onLoad
16985 * so command out it
16990 this.selectedIndex = -1;
16995 this.loadNext = false;
16999 getParams : function(q){
17001 //p[this.queryParam] = q;
17005 p.limit = this.pageSize;
17011 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17013 collapse : function(){
17014 if(!this.isExpanded()){
17020 this.hasFocus = false;
17024 this.cancelBtn.hide();
17025 this.trigger.show();
17028 this.tickableInputEl().dom.value = '';
17029 this.tickableInputEl().blur();
17034 Roo.get(document).un('mousedown', this.collapseIf, this);
17035 Roo.get(document).un('mousewheel', this.collapseIf, this);
17036 if (!this.editable) {
17037 Roo.get(document).un('keydown', this.listKeyPress, this);
17039 this.fireEvent('collapse', this);
17045 collapseIf : function(e){
17046 var in_combo = e.within(this.el);
17047 var in_list = e.within(this.list);
17048 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17050 if (in_combo || in_list || is_list) {
17051 //e.stopPropagation();
17056 this.onTickableFooterButtonClick(e, false, false);
17064 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17066 expand : function(){
17068 if(this.isExpanded() || !this.hasFocus){
17072 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17073 this.list.setWidth(lw);
17079 this.restrictHeight();
17083 this.tickItems = Roo.apply([], this.item);
17086 this.cancelBtn.show();
17087 this.trigger.hide();
17090 this.tickableInputEl().focus();
17095 Roo.get(document).on('mousedown', this.collapseIf, this);
17096 Roo.get(document).on('mousewheel', this.collapseIf, this);
17097 if (!this.editable) {
17098 Roo.get(document).on('keydown', this.listKeyPress, this);
17101 this.fireEvent('expand', this);
17105 // Implements the default empty TriggerField.onTriggerClick function
17106 onTriggerClick : function(e)
17108 Roo.log('trigger click');
17110 if(this.disabled || !this.triggerList){
17115 this.loadNext = false;
17117 if(this.isExpanded()){
17119 if (!this.blockFocus) {
17120 this.inputEl().focus();
17124 this.hasFocus = true;
17125 if(this.triggerAction == 'all') {
17126 this.doQuery(this.allQuery, true);
17128 this.doQuery(this.getRawValue());
17130 if (!this.blockFocus) {
17131 this.inputEl().focus();
17136 onTickableTriggerClick : function(e)
17143 this.loadNext = false;
17144 this.hasFocus = true;
17146 if(this.triggerAction == 'all') {
17147 this.doQuery(this.allQuery, true);
17149 this.doQuery(this.getRawValue());
17153 onSearchFieldClick : function(e)
17155 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17156 this.onTickableFooterButtonClick(e, false, false);
17160 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17165 this.loadNext = false;
17166 this.hasFocus = true;
17168 if(this.triggerAction == 'all') {
17169 this.doQuery(this.allQuery, true);
17171 this.doQuery(this.getRawValue());
17175 listKeyPress : function(e)
17177 //Roo.log('listkeypress');
17178 // scroll to first matching element based on key pres..
17179 if (e.isSpecialKey()) {
17182 var k = String.fromCharCode(e.getKey()).toUpperCase();
17185 var csel = this.view.getSelectedNodes();
17186 var cselitem = false;
17188 var ix = this.view.indexOf(csel[0]);
17189 cselitem = this.store.getAt(ix);
17190 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17196 this.store.each(function(v) {
17198 // start at existing selection.
17199 if (cselitem.id == v.id) {
17205 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17206 match = this.store.indexOf(v);
17212 if (match === false) {
17213 return true; // no more action?
17216 this.view.select(match);
17217 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17218 sn.scrollIntoView(sn.dom.parentNode, false);
17221 onViewScroll : function(e, t){
17223 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){
17227 this.hasQuery = true;
17229 this.loading = this.list.select('.loading', true).first();
17231 if(this.loading === null){
17232 this.list.createChild({
17234 cls: 'loading roo-select2-more-results roo-select2-active',
17235 html: 'Loading more results...'
17238 this.loading = this.list.select('.loading', true).first();
17240 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17242 this.loading.hide();
17245 this.loading.show();
17250 this.loadNext = true;
17252 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17257 addItem : function(o)
17259 var dv = ''; // display value
17261 if (this.displayField) {
17262 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17264 // this is an error condition!!!
17265 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17272 var choice = this.choices.createChild({
17274 cls: 'roo-select2-search-choice',
17283 cls: 'roo-select2-search-choice-close fa fa-times',
17288 }, this.searchField);
17290 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17292 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17300 this.inputEl().dom.value = '';
17305 onRemoveItem : function(e, _self, o)
17307 e.preventDefault();
17309 this.lastItem = Roo.apply([], this.item);
17311 var index = this.item.indexOf(o.data) * 1;
17314 Roo.log('not this item?!');
17318 this.item.splice(index, 1);
17323 this.fireEvent('remove', this, e);
17329 syncValue : function()
17331 if(!this.item.length){
17338 Roo.each(this.item, function(i){
17339 if(_this.valueField){
17340 value.push(i[_this.valueField]);
17347 this.value = value.join(',');
17349 if(this.hiddenField){
17350 this.hiddenField.dom.value = this.value;
17353 this.store.fireEvent("datachanged", this.store);
17358 clearItem : function()
17360 if(!this.multiple){
17366 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17374 if(this.tickable && !Roo.isTouch){
17375 this.view.refresh();
17379 inputEl: function ()
17381 if(Roo.isIOS && this.useNativeIOS){
17382 return this.el.select('select.roo-ios-select', true).first();
17385 if(Roo.isTouch && this.mobileTouchView){
17386 return this.el.select('input.form-control',true).first();
17390 return this.searchField;
17393 return this.el.select('input.form-control',true).first();
17396 onTickableFooterButtonClick : function(e, btn, el)
17398 e.preventDefault();
17400 this.lastItem = Roo.apply([], this.item);
17402 if(btn && btn.name == 'cancel'){
17403 this.tickItems = Roo.apply([], this.item);
17412 Roo.each(this.tickItems, function(o){
17420 validate : function()
17422 if(this.getVisibilityEl().hasClass('hidden')){
17426 var v = this.getRawValue();
17429 v = this.getValue();
17432 if(this.disabled || this.allowBlank || v.length){
17437 this.markInvalid();
17441 tickableInputEl : function()
17443 if(!this.tickable || !this.editable){
17444 return this.inputEl();
17447 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17451 getAutoCreateTouchView : function()
17456 cls: 'form-group' //input-group
17462 type : this.inputType,
17463 cls : 'form-control x-combo-noedit',
17464 autocomplete: 'new-password',
17465 placeholder : this.placeholder || '',
17470 input.name = this.name;
17474 input.cls += ' input-' + this.size;
17477 if (this.disabled) {
17478 input.disabled = true;
17482 cls : 'roo-combobox-wrap',
17489 inputblock.cls += ' input-group';
17491 inputblock.cn.unshift({
17493 cls : 'input-group-addon input-group-prepend input-group-text',
17498 if(this.removable && !this.multiple){
17499 inputblock.cls += ' roo-removable';
17501 inputblock.cn.push({
17504 cls : 'roo-combo-removable-btn close'
17508 if(this.hasFeedback && !this.allowBlank){
17510 inputblock.cls += ' has-feedback';
17512 inputblock.cn.push({
17514 cls: 'glyphicon form-control-feedback'
17521 inputblock.cls += (this.before) ? '' : ' input-group';
17523 inputblock.cn.push({
17525 cls : 'input-group-addon input-group-append input-group-text',
17531 var ibwrap = inputblock;
17536 cls: 'roo-select2-choices',
17540 cls: 'roo-select2-search-field',
17553 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17558 cls: 'form-hidden-field'
17564 if(!this.multiple && this.showToggleBtn){
17570 if (this.caret != false) {
17573 cls: 'fa fa-' + this.caret
17580 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17582 Roo.bootstrap.version == 3 ? caret : '',
17585 cls: 'combobox-clear',
17599 combobox.cls += ' roo-select2-container-multi';
17602 var required = this.allowBlank ? {
17604 style: 'display: none'
17607 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17608 tooltip : 'This field is required'
17611 var align = this.labelAlign || this.parentLabelAlign();
17613 if (align ==='left' && this.fieldLabel.length) {
17619 cls : 'control-label col-form-label',
17620 html : this.fieldLabel
17624 cls : 'roo-combobox-wrap ',
17631 var labelCfg = cfg.cn[1];
17632 var contentCfg = cfg.cn[2];
17635 if(this.indicatorpos == 'right'){
17640 cls : 'control-label col-form-label',
17644 html : this.fieldLabel
17650 cls : "roo-combobox-wrap ",
17658 labelCfg = cfg.cn[0];
17659 contentCfg = cfg.cn[1];
17664 if(this.labelWidth > 12){
17665 labelCfg.style = "width: " + this.labelWidth + 'px';
17668 if(this.labelWidth < 13 && this.labelmd == 0){
17669 this.labelmd = this.labelWidth;
17672 if(this.labellg > 0){
17673 labelCfg.cls += ' col-lg-' + this.labellg;
17674 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17677 if(this.labelmd > 0){
17678 labelCfg.cls += ' col-md-' + this.labelmd;
17679 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17682 if(this.labelsm > 0){
17683 labelCfg.cls += ' col-sm-' + this.labelsm;
17684 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17687 if(this.labelxs > 0){
17688 labelCfg.cls += ' col-xs-' + this.labelxs;
17689 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17693 } else if ( this.fieldLabel.length) {
17698 cls : 'control-label',
17699 html : this.fieldLabel
17710 if(this.indicatorpos == 'right'){
17714 cls : 'control-label',
17715 html : this.fieldLabel,
17733 var settings = this;
17735 ['xs','sm','md','lg'].map(function(size){
17736 if (settings[size]) {
17737 cfg.cls += ' col-' + size + '-' + settings[size];
17744 initTouchView : function()
17746 this.renderTouchView();
17748 this.touchViewEl.on('scroll', function(){
17749 this.el.dom.scrollTop = 0;
17752 this.originalValue = this.getValue();
17754 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17756 this.inputEl().on("click", this.showTouchView, this);
17757 if (this.triggerEl) {
17758 this.triggerEl.on("click", this.showTouchView, this);
17762 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17763 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17765 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17767 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17768 this.store.on('load', this.onTouchViewLoad, this);
17769 this.store.on('loadexception', this.onTouchViewLoadException, this);
17771 if(this.hiddenName){
17773 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17775 this.hiddenField.dom.value =
17776 this.hiddenValue !== undefined ? this.hiddenValue :
17777 this.value !== undefined ? this.value : '';
17779 this.el.dom.removeAttribute('name');
17780 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17784 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17785 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17788 if(this.removable && !this.multiple){
17789 var close = this.closeTriggerEl();
17791 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17792 close.on('click', this.removeBtnClick, this, close);
17796 * fix the bug in Safari iOS8
17798 this.inputEl().on("focus", function(e){
17799 document.activeElement.blur();
17802 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17809 renderTouchView : function()
17811 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17812 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17814 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17815 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17817 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17818 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17819 this.touchViewBodyEl.setStyle('overflow', 'auto');
17821 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17822 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17824 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17825 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17829 showTouchView : function()
17835 this.touchViewHeaderEl.hide();
17837 if(this.modalTitle.length){
17838 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17839 this.touchViewHeaderEl.show();
17842 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17843 this.touchViewEl.show();
17845 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17847 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17848 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17850 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17852 if(this.modalTitle.length){
17853 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17856 this.touchViewBodyEl.setHeight(bodyHeight);
17860 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17862 this.touchViewEl.addClass(['in','show']);
17865 if(this._touchViewMask){
17866 Roo.get(document.body).addClass("x-body-masked");
17867 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17868 this._touchViewMask.setStyle('z-index', 10000);
17869 this._touchViewMask.addClass('show');
17872 this.doTouchViewQuery();
17876 hideTouchView : function()
17878 this.touchViewEl.removeClass(['in','show']);
17882 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17884 this.touchViewEl.setStyle('display', 'none');
17887 if(this._touchViewMask){
17888 this._touchViewMask.removeClass('show');
17889 Roo.get(document.body).removeClass("x-body-masked");
17893 setTouchViewValue : function()
17900 Roo.each(this.tickItems, function(o){
17905 this.hideTouchView();
17908 doTouchViewQuery : function()
17917 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17921 if(!this.alwaysQuery || this.mode == 'local'){
17922 this.onTouchViewLoad();
17929 onTouchViewBeforeLoad : function(combo,opts)
17935 onTouchViewLoad : function()
17937 if(this.store.getCount() < 1){
17938 this.onTouchViewEmptyResults();
17942 this.clearTouchView();
17944 var rawValue = this.getRawValue();
17946 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17948 this.tickItems = [];
17950 this.store.data.each(function(d, rowIndex){
17951 var row = this.touchViewListGroup.createChild(template);
17953 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17954 row.addClass(d.data.cls);
17957 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17960 html : d.data[this.displayField]
17963 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17964 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17967 row.removeClass('selected');
17968 if(!this.multiple && this.valueField &&
17969 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17972 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17973 row.addClass('selected');
17976 if(this.multiple && this.valueField &&
17977 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17981 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17982 this.tickItems.push(d.data);
17985 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17989 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17991 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17993 if(this.modalTitle.length){
17994 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17997 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17999 if(this.mobile_restrict_height && listHeight < bodyHeight){
18000 this.touchViewBodyEl.setHeight(listHeight);
18005 if(firstChecked && listHeight > bodyHeight){
18006 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18011 onTouchViewLoadException : function()
18013 this.hideTouchView();
18016 onTouchViewEmptyResults : function()
18018 this.clearTouchView();
18020 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18022 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18026 clearTouchView : function()
18028 this.touchViewListGroup.dom.innerHTML = '';
18031 onTouchViewClick : function(e, el, o)
18033 e.preventDefault();
18036 var rowIndex = o.rowIndex;
18038 var r = this.store.getAt(rowIndex);
18040 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18042 if(!this.multiple){
18043 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18044 c.dom.removeAttribute('checked');
18047 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18049 this.setFromData(r.data);
18051 var close = this.closeTriggerEl();
18057 this.hideTouchView();
18059 this.fireEvent('select', this, r, rowIndex);
18064 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18065 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18066 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18070 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18071 this.addItem(r.data);
18072 this.tickItems.push(r.data);
18076 getAutoCreateNativeIOS : function()
18079 cls: 'form-group' //input-group,
18084 cls : 'roo-ios-select'
18088 combobox.name = this.name;
18091 if (this.disabled) {
18092 combobox.disabled = true;
18095 var settings = this;
18097 ['xs','sm','md','lg'].map(function(size){
18098 if (settings[size]) {
18099 cfg.cls += ' col-' + size + '-' + settings[size];
18109 initIOSView : function()
18111 this.store.on('load', this.onIOSViewLoad, this);
18116 onIOSViewLoad : function()
18118 if(this.store.getCount() < 1){
18122 this.clearIOSView();
18124 if(this.allowBlank) {
18126 var default_text = '-- SELECT --';
18128 if(this.placeholder.length){
18129 default_text = this.placeholder;
18132 if(this.emptyTitle.length){
18133 default_text += ' - ' + this.emptyTitle + ' -';
18136 var opt = this.inputEl().createChild({
18139 html : default_text
18143 o[this.valueField] = 0;
18144 o[this.displayField] = default_text;
18146 this.ios_options.push({
18153 this.store.data.each(function(d, rowIndex){
18157 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18158 html = d.data[this.displayField];
18163 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18164 value = d.data[this.valueField];
18173 if(this.value == d.data[this.valueField]){
18174 option['selected'] = true;
18177 var opt = this.inputEl().createChild(option);
18179 this.ios_options.push({
18186 this.inputEl().on('change', function(){
18187 this.fireEvent('select', this);
18192 clearIOSView: function()
18194 this.inputEl().dom.innerHTML = '';
18196 this.ios_options = [];
18199 setIOSValue: function(v)
18203 if(!this.ios_options){
18207 Roo.each(this.ios_options, function(opts){
18209 opts.el.dom.removeAttribute('selected');
18211 if(opts.data[this.valueField] != v){
18215 opts.el.dom.setAttribute('selected', true);
18221 * @cfg {Boolean} grow
18225 * @cfg {Number} growMin
18229 * @cfg {Number} growMax
18238 Roo.apply(Roo.bootstrap.ComboBox, {
18242 cls: 'modal-header',
18264 cls: 'list-group-item',
18268 cls: 'roo-combobox-list-group-item-value'
18272 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18286 listItemCheckbox : {
18288 cls: 'list-group-item',
18292 cls: 'roo-combobox-list-group-item-value'
18296 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18312 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18317 cls: 'modal-footer',
18325 cls: 'col-xs-6 text-left',
18328 cls: 'btn btn-danger roo-touch-view-cancel',
18334 cls: 'col-xs-6 text-right',
18337 cls: 'btn btn-success roo-touch-view-ok',
18348 Roo.apply(Roo.bootstrap.ComboBox, {
18350 touchViewTemplate : {
18352 cls: 'modal fade roo-combobox-touch-view',
18356 cls: 'modal-dialog',
18357 style : 'position:fixed', // we have to fix position....
18361 cls: 'modal-content',
18363 Roo.bootstrap.ComboBox.header,
18364 Roo.bootstrap.ComboBox.body,
18365 Roo.bootstrap.ComboBox.footer
18374 * Ext JS Library 1.1.1
18375 * Copyright(c) 2006-2007, Ext JS, LLC.
18377 * Originally Released Under LGPL - original licence link has changed is not relivant.
18380 * <script type="text/javascript">
18385 * @extends Roo.util.Observable
18386 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18387 * This class also supports single and multi selection modes. <br>
18388 * Create a data model bound view:
18390 var store = new Roo.data.Store(...);
18392 var view = new Roo.View({
18394 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18396 singleSelect: true,
18397 selectedClass: "ydataview-selected",
18401 // listen for node click?
18402 view.on("click", function(vw, index, node, e){
18403 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18407 dataModel.load("foobar.xml");
18409 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18411 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18412 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18414 * Note: old style constructor is still suported (container, template, config)
18417 * Create a new View
18418 * @param {Object} config The config object
18421 Roo.View = function(config, depreciated_tpl, depreciated_config){
18423 this.parent = false;
18425 if (typeof(depreciated_tpl) == 'undefined') {
18426 // new way.. - universal constructor.
18427 Roo.apply(this, config);
18428 this.el = Roo.get(this.el);
18431 this.el = Roo.get(config);
18432 this.tpl = depreciated_tpl;
18433 Roo.apply(this, depreciated_config);
18435 this.wrapEl = this.el.wrap().wrap();
18436 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18439 if(typeof(this.tpl) == "string"){
18440 this.tpl = new Roo.Template(this.tpl);
18442 // support xtype ctors..
18443 this.tpl = new Roo.factory(this.tpl, Roo);
18447 this.tpl.compile();
18452 * @event beforeclick
18453 * Fires before a click is processed. Returns false to cancel the default action.
18454 * @param {Roo.View} this
18455 * @param {Number} index The index of the target node
18456 * @param {HTMLElement} node The target node
18457 * @param {Roo.EventObject} e The raw event object
18459 "beforeclick" : true,
18462 * Fires when a template node is clicked.
18463 * @param {Roo.View} this
18464 * @param {Number} index The index of the target node
18465 * @param {HTMLElement} node The target node
18466 * @param {Roo.EventObject} e The raw event object
18471 * Fires when a template node is double clicked.
18472 * @param {Roo.View} this
18473 * @param {Number} index The index of the target node
18474 * @param {HTMLElement} node The target node
18475 * @param {Roo.EventObject} e The raw event object
18479 * @event contextmenu
18480 * Fires when a template node is right clicked.
18481 * @param {Roo.View} this
18482 * @param {Number} index The index of the target node
18483 * @param {HTMLElement} node The target node
18484 * @param {Roo.EventObject} e The raw event object
18486 "contextmenu" : true,
18488 * @event selectionchange
18489 * Fires when the selected nodes change.
18490 * @param {Roo.View} this
18491 * @param {Array} selections Array of the selected nodes
18493 "selectionchange" : true,
18496 * @event beforeselect
18497 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18498 * @param {Roo.View} this
18499 * @param {HTMLElement} node The node to be selected
18500 * @param {Array} selections Array of currently selected nodes
18502 "beforeselect" : true,
18504 * @event preparedata
18505 * Fires on every row to render, to allow you to change the data.
18506 * @param {Roo.View} this
18507 * @param {Object} data to be rendered (change this)
18509 "preparedata" : true
18517 "click": this.onClick,
18518 "dblclick": this.onDblClick,
18519 "contextmenu": this.onContextMenu,
18523 this.selections = [];
18525 this.cmp = new Roo.CompositeElementLite([]);
18527 this.store = Roo.factory(this.store, Roo.data);
18528 this.setStore(this.store, true);
18531 if ( this.footer && this.footer.xtype) {
18533 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18535 this.footer.dataSource = this.store;
18536 this.footer.container = fctr;
18537 this.footer = Roo.factory(this.footer, Roo);
18538 fctr.insertFirst(this.el);
18540 // this is a bit insane - as the paging toolbar seems to detach the el..
18541 // dom.parentNode.parentNode.parentNode
18542 // they get detached?
18546 Roo.View.superclass.constructor.call(this);
18551 Roo.extend(Roo.View, Roo.util.Observable, {
18554 * @cfg {Roo.data.Store} store Data store to load data from.
18559 * @cfg {String|Roo.Element} el The container element.
18564 * @cfg {String|Roo.Template} tpl The template used by this View
18568 * @cfg {String} dataName the named area of the template to use as the data area
18569 * Works with domtemplates roo-name="name"
18573 * @cfg {String} selectedClass The css class to add to selected nodes
18575 selectedClass : "x-view-selected",
18577 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18582 * @cfg {String} text to display on mask (default Loading)
18586 * @cfg {Boolean} multiSelect Allow multiple selection
18588 multiSelect : false,
18590 * @cfg {Boolean} singleSelect Allow single selection
18592 singleSelect: false,
18595 * @cfg {Boolean} toggleSelect - selecting
18597 toggleSelect : false,
18600 * @cfg {Boolean} tickable - selecting
18605 * Returns the element this view is bound to.
18606 * @return {Roo.Element}
18608 getEl : function(){
18609 return this.wrapEl;
18615 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18617 refresh : function(){
18618 //Roo.log('refresh');
18621 // if we are using something like 'domtemplate', then
18622 // the what gets used is:
18623 // t.applySubtemplate(NAME, data, wrapping data..)
18624 // the outer template then get' applied with
18625 // the store 'extra data'
18626 // and the body get's added to the
18627 // roo-name="data" node?
18628 // <span class='roo-tpl-{name}'></span> ?????
18632 this.clearSelections();
18633 this.el.update("");
18635 var records = this.store.getRange();
18636 if(records.length < 1) {
18638 // is this valid?? = should it render a template??
18640 this.el.update(this.emptyText);
18644 if (this.dataName) {
18645 this.el.update(t.apply(this.store.meta)); //????
18646 el = this.el.child('.roo-tpl-' + this.dataName);
18649 for(var i = 0, len = records.length; i < len; i++){
18650 var data = this.prepareData(records[i].data, i, records[i]);
18651 this.fireEvent("preparedata", this, data, i, records[i]);
18653 var d = Roo.apply({}, data);
18656 Roo.apply(d, {'roo-id' : Roo.id()});
18660 Roo.each(this.parent.item, function(item){
18661 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18664 Roo.apply(d, {'roo-data-checked' : 'checked'});
18668 html[html.length] = Roo.util.Format.trim(
18670 t.applySubtemplate(this.dataName, d, this.store.meta) :
18677 el.update(html.join(""));
18678 this.nodes = el.dom.childNodes;
18679 this.updateIndexes(0);
18684 * Function to override to reformat the data that is sent to
18685 * the template for each node.
18686 * DEPRICATED - use the preparedata event handler.
18687 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18688 * a JSON object for an UpdateManager bound view).
18690 prepareData : function(data, index, record)
18692 this.fireEvent("preparedata", this, data, index, record);
18696 onUpdate : function(ds, record){
18697 // Roo.log('on update');
18698 this.clearSelections();
18699 var index = this.store.indexOf(record);
18700 var n = this.nodes[index];
18701 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18702 n.parentNode.removeChild(n);
18703 this.updateIndexes(index, index);
18709 onAdd : function(ds, records, index)
18711 //Roo.log(['on Add', ds, records, index] );
18712 this.clearSelections();
18713 if(this.nodes.length == 0){
18717 var n = this.nodes[index];
18718 for(var i = 0, len = records.length; i < len; i++){
18719 var d = this.prepareData(records[i].data, i, records[i]);
18721 this.tpl.insertBefore(n, d);
18724 this.tpl.append(this.el, d);
18727 this.updateIndexes(index);
18730 onRemove : function(ds, record, index){
18731 // Roo.log('onRemove');
18732 this.clearSelections();
18733 var el = this.dataName ?
18734 this.el.child('.roo-tpl-' + this.dataName) :
18737 el.dom.removeChild(this.nodes[index]);
18738 this.updateIndexes(index);
18742 * Refresh an individual node.
18743 * @param {Number} index
18745 refreshNode : function(index){
18746 this.onUpdate(this.store, this.store.getAt(index));
18749 updateIndexes : function(startIndex, endIndex){
18750 var ns = this.nodes;
18751 startIndex = startIndex || 0;
18752 endIndex = endIndex || ns.length - 1;
18753 for(var i = startIndex; i <= endIndex; i++){
18754 ns[i].nodeIndex = i;
18759 * Changes the data store this view uses and refresh the view.
18760 * @param {Store} store
18762 setStore : function(store, initial){
18763 if(!initial && this.store){
18764 this.store.un("datachanged", this.refresh);
18765 this.store.un("add", this.onAdd);
18766 this.store.un("remove", this.onRemove);
18767 this.store.un("update", this.onUpdate);
18768 this.store.un("clear", this.refresh);
18769 this.store.un("beforeload", this.onBeforeLoad);
18770 this.store.un("load", this.onLoad);
18771 this.store.un("loadexception", this.onLoad);
18775 store.on("datachanged", this.refresh, this);
18776 store.on("add", this.onAdd, this);
18777 store.on("remove", this.onRemove, this);
18778 store.on("update", this.onUpdate, this);
18779 store.on("clear", this.refresh, this);
18780 store.on("beforeload", this.onBeforeLoad, this);
18781 store.on("load", this.onLoad, this);
18782 store.on("loadexception", this.onLoad, this);
18790 * onbeforeLoad - masks the loading area.
18793 onBeforeLoad : function(store,opts)
18795 //Roo.log('onBeforeLoad');
18797 this.el.update("");
18799 this.el.mask(this.mask ? this.mask : "Loading" );
18801 onLoad : function ()
18808 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18809 * @param {HTMLElement} node
18810 * @return {HTMLElement} The template node
18812 findItemFromChild : function(node){
18813 var el = this.dataName ?
18814 this.el.child('.roo-tpl-' + this.dataName,true) :
18817 if(!node || node.parentNode == el){
18820 var p = node.parentNode;
18821 while(p && p != el){
18822 if(p.parentNode == el){
18831 onClick : function(e){
18832 var item = this.findItemFromChild(e.getTarget());
18834 var index = this.indexOf(item);
18835 if(this.onItemClick(item, index, e) !== false){
18836 this.fireEvent("click", this, index, item, e);
18839 this.clearSelections();
18844 onContextMenu : function(e){
18845 var item = this.findItemFromChild(e.getTarget());
18847 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18852 onDblClick : function(e){
18853 var item = this.findItemFromChild(e.getTarget());
18855 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18859 onItemClick : function(item, index, e)
18861 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18864 if (this.toggleSelect) {
18865 var m = this.isSelected(item) ? 'unselect' : 'select';
18868 _t[m](item, true, false);
18871 if(this.multiSelect || this.singleSelect){
18872 if(this.multiSelect && e.shiftKey && this.lastSelection){
18873 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18875 this.select(item, this.multiSelect && e.ctrlKey);
18876 this.lastSelection = item;
18879 if(!this.tickable){
18880 e.preventDefault();
18888 * Get the number of selected nodes.
18891 getSelectionCount : function(){
18892 return this.selections.length;
18896 * Get the currently selected nodes.
18897 * @return {Array} An array of HTMLElements
18899 getSelectedNodes : function(){
18900 return this.selections;
18904 * Get the indexes of the selected nodes.
18907 getSelectedIndexes : function(){
18908 var indexes = [], s = this.selections;
18909 for(var i = 0, len = s.length; i < len; i++){
18910 indexes.push(s[i].nodeIndex);
18916 * Clear all selections
18917 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18919 clearSelections : function(suppressEvent){
18920 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18921 this.cmp.elements = this.selections;
18922 this.cmp.removeClass(this.selectedClass);
18923 this.selections = [];
18924 if(!suppressEvent){
18925 this.fireEvent("selectionchange", this, this.selections);
18931 * Returns true if the passed node is selected
18932 * @param {HTMLElement/Number} node The node or node index
18933 * @return {Boolean}
18935 isSelected : function(node){
18936 var s = this.selections;
18940 node = this.getNode(node);
18941 return s.indexOf(node) !== -1;
18946 * @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
18947 * @param {Boolean} keepExisting (optional) true to keep existing selections
18948 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18950 select : function(nodeInfo, keepExisting, suppressEvent){
18951 if(nodeInfo instanceof Array){
18953 this.clearSelections(true);
18955 for(var i = 0, len = nodeInfo.length; i < len; i++){
18956 this.select(nodeInfo[i], true, true);
18960 var node = this.getNode(nodeInfo);
18961 if(!node || this.isSelected(node)){
18962 return; // already selected.
18965 this.clearSelections(true);
18968 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18969 Roo.fly(node).addClass(this.selectedClass);
18970 this.selections.push(node);
18971 if(!suppressEvent){
18972 this.fireEvent("selectionchange", this, this.selections);
18980 * @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
18981 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18982 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18984 unselect : function(nodeInfo, keepExisting, suppressEvent)
18986 if(nodeInfo instanceof Array){
18987 Roo.each(this.selections, function(s) {
18988 this.unselect(s, nodeInfo);
18992 var node = this.getNode(nodeInfo);
18993 if(!node || !this.isSelected(node)){
18994 //Roo.log("not selected");
18995 return; // not selected.
18999 Roo.each(this.selections, function(s) {
19001 Roo.fly(node).removeClass(this.selectedClass);
19008 this.selections= ns;
19009 this.fireEvent("selectionchange", this, this.selections);
19013 * Gets a template node.
19014 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19015 * @return {HTMLElement} The node or null if it wasn't found
19017 getNode : function(nodeInfo){
19018 if(typeof nodeInfo == "string"){
19019 return document.getElementById(nodeInfo);
19020 }else if(typeof nodeInfo == "number"){
19021 return this.nodes[nodeInfo];
19027 * Gets a range template nodes.
19028 * @param {Number} startIndex
19029 * @param {Number} endIndex
19030 * @return {Array} An array of nodes
19032 getNodes : function(start, end){
19033 var ns = this.nodes;
19034 start = start || 0;
19035 end = typeof end == "undefined" ? ns.length - 1 : end;
19038 for(var i = start; i <= end; i++){
19042 for(var i = start; i >= end; i--){
19050 * Finds the index of the passed node
19051 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19052 * @return {Number} The index of the node or -1
19054 indexOf : function(node){
19055 node = this.getNode(node);
19056 if(typeof node.nodeIndex == "number"){
19057 return node.nodeIndex;
19059 var ns = this.nodes;
19060 for(var i = 0, len = ns.length; i < len; i++){
19071 * based on jquery fullcalendar
19075 Roo.bootstrap = Roo.bootstrap || {};
19077 * @class Roo.bootstrap.Calendar
19078 * @extends Roo.bootstrap.Component
19079 * Bootstrap Calendar class
19080 * @cfg {Boolean} loadMask (true|false) default false
19081 * @cfg {Object} header generate the user specific header of the calendar, default false
19084 * Create a new Container
19085 * @param {Object} config The config object
19090 Roo.bootstrap.Calendar = function(config){
19091 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19095 * Fires when a date is selected
19096 * @param {DatePicker} this
19097 * @param {Date} date The selected date
19101 * @event monthchange
19102 * Fires when the displayed month changes
19103 * @param {DatePicker} this
19104 * @param {Date} date The selected month
19106 'monthchange': true,
19108 * @event evententer
19109 * Fires when mouse over an event
19110 * @param {Calendar} this
19111 * @param {event} Event
19113 'evententer': true,
19115 * @event eventleave
19116 * Fires when the mouse leaves an
19117 * @param {Calendar} this
19120 'eventleave': true,
19122 * @event eventclick
19123 * Fires when the mouse click an
19124 * @param {Calendar} this
19133 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19136 * @cfg {Number} startDay
19137 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19145 getAutoCreate : function(){
19148 var fc_button = function(name, corner, style, content ) {
19149 return Roo.apply({},{
19151 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19153 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19156 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19167 style : 'width:100%',
19174 cls : 'fc-header-left',
19176 fc_button('prev', 'left', 'arrow', '‹' ),
19177 fc_button('next', 'right', 'arrow', '›' ),
19178 { tag: 'span', cls: 'fc-header-space' },
19179 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19187 cls : 'fc-header-center',
19191 cls: 'fc-header-title',
19194 html : 'month / year'
19202 cls : 'fc-header-right',
19204 /* fc_button('month', 'left', '', 'month' ),
19205 fc_button('week', '', '', 'week' ),
19206 fc_button('day', 'right', '', 'day' )
19218 header = this.header;
19221 var cal_heads = function() {
19223 // fixme - handle this.
19225 for (var i =0; i < Date.dayNames.length; i++) {
19226 var d = Date.dayNames[i];
19229 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19230 html : d.substring(0,3)
19234 ret[0].cls += ' fc-first';
19235 ret[6].cls += ' fc-last';
19238 var cal_cell = function(n) {
19241 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19246 cls: 'fc-day-number',
19250 cls: 'fc-day-content',
19254 style: 'position: relative;' // height: 17px;
19266 var cal_rows = function() {
19269 for (var r = 0; r < 6; r++) {
19276 for (var i =0; i < Date.dayNames.length; i++) {
19277 var d = Date.dayNames[i];
19278 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19281 row.cn[0].cls+=' fc-first';
19282 row.cn[0].cn[0].style = 'min-height:90px';
19283 row.cn[6].cls+=' fc-last';
19287 ret[0].cls += ' fc-first';
19288 ret[4].cls += ' fc-prev-last';
19289 ret[5].cls += ' fc-last';
19296 cls: 'fc-border-separate',
19297 style : 'width:100%',
19305 cls : 'fc-first fc-last',
19323 cls : 'fc-content',
19324 style : "position: relative;",
19327 cls : 'fc-view fc-view-month fc-grid',
19328 style : 'position: relative',
19329 unselectable : 'on',
19332 cls : 'fc-event-container',
19333 style : 'position:absolute;z-index:8;top:0;left:0;'
19351 initEvents : function()
19354 throw "can not find store for calendar";
19360 style: "text-align:center",
19364 style: "background-color:white;width:50%;margin:250 auto",
19368 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19379 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19381 var size = this.el.select('.fc-content', true).first().getSize();
19382 this.maskEl.setSize(size.width, size.height);
19383 this.maskEl.enableDisplayMode("block");
19384 if(!this.loadMask){
19385 this.maskEl.hide();
19388 this.store = Roo.factory(this.store, Roo.data);
19389 this.store.on('load', this.onLoad, this);
19390 this.store.on('beforeload', this.onBeforeLoad, this);
19394 this.cells = this.el.select('.fc-day',true);
19395 //Roo.log(this.cells);
19396 this.textNodes = this.el.query('.fc-day-number');
19397 this.cells.addClassOnOver('fc-state-hover');
19399 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19400 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19401 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19402 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19404 this.on('monthchange', this.onMonthChange, this);
19406 this.update(new Date().clearTime());
19409 resize : function() {
19410 var sz = this.el.getSize();
19412 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19413 this.el.select('.fc-day-content div',true).setHeight(34);
19418 showPrevMonth : function(e){
19419 this.update(this.activeDate.add("mo", -1));
19421 showToday : function(e){
19422 this.update(new Date().clearTime());
19425 showNextMonth : function(e){
19426 this.update(this.activeDate.add("mo", 1));
19430 showPrevYear : function(){
19431 this.update(this.activeDate.add("y", -1));
19435 showNextYear : function(){
19436 this.update(this.activeDate.add("y", 1));
19441 update : function(date)
19443 var vd = this.activeDate;
19444 this.activeDate = date;
19445 // if(vd && this.el){
19446 // var t = date.getTime();
19447 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19448 // Roo.log('using add remove');
19450 // this.fireEvent('monthchange', this, date);
19452 // this.cells.removeClass("fc-state-highlight");
19453 // this.cells.each(function(c){
19454 // if(c.dateValue == t){
19455 // c.addClass("fc-state-highlight");
19456 // setTimeout(function(){
19457 // try{c.dom.firstChild.focus();}catch(e){}
19467 var days = date.getDaysInMonth();
19469 var firstOfMonth = date.getFirstDateOfMonth();
19470 var startingPos = firstOfMonth.getDay()-this.startDay;
19472 if(startingPos < this.startDay){
19476 var pm = date.add(Date.MONTH, -1);
19477 var prevStart = pm.getDaysInMonth()-startingPos;
19479 this.cells = this.el.select('.fc-day',true);
19480 this.textNodes = this.el.query('.fc-day-number');
19481 this.cells.addClassOnOver('fc-state-hover');
19483 var cells = this.cells.elements;
19484 var textEls = this.textNodes;
19486 Roo.each(cells, function(cell){
19487 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19490 days += startingPos;
19492 // convert everything to numbers so it's fast
19493 var day = 86400000;
19494 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19497 //Roo.log(prevStart);
19499 var today = new Date().clearTime().getTime();
19500 var sel = date.clearTime().getTime();
19501 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19502 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19503 var ddMatch = this.disabledDatesRE;
19504 var ddText = this.disabledDatesText;
19505 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19506 var ddaysText = this.disabledDaysText;
19507 var format = this.format;
19509 var setCellClass = function(cal, cell){
19513 //Roo.log('set Cell Class');
19515 var t = d.getTime();
19519 cell.dateValue = t;
19521 cell.className += " fc-today";
19522 cell.className += " fc-state-highlight";
19523 cell.title = cal.todayText;
19526 // disable highlight in other month..
19527 //cell.className += " fc-state-highlight";
19532 cell.className = " fc-state-disabled";
19533 cell.title = cal.minText;
19537 cell.className = " fc-state-disabled";
19538 cell.title = cal.maxText;
19542 if(ddays.indexOf(d.getDay()) != -1){
19543 cell.title = ddaysText;
19544 cell.className = " fc-state-disabled";
19547 if(ddMatch && format){
19548 var fvalue = d.dateFormat(format);
19549 if(ddMatch.test(fvalue)){
19550 cell.title = ddText.replace("%0", fvalue);
19551 cell.className = " fc-state-disabled";
19555 if (!cell.initialClassName) {
19556 cell.initialClassName = cell.dom.className;
19559 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19564 for(; i < startingPos; i++) {
19565 textEls[i].innerHTML = (++prevStart);
19566 d.setDate(d.getDate()+1);
19568 cells[i].className = "fc-past fc-other-month";
19569 setCellClass(this, cells[i]);
19574 for(; i < days; i++){
19575 intDay = i - startingPos + 1;
19576 textEls[i].innerHTML = (intDay);
19577 d.setDate(d.getDate()+1);
19579 cells[i].className = ''; // "x-date-active";
19580 setCellClass(this, cells[i]);
19584 for(; i < 42; i++) {
19585 textEls[i].innerHTML = (++extraDays);
19586 d.setDate(d.getDate()+1);
19588 cells[i].className = "fc-future fc-other-month";
19589 setCellClass(this, cells[i]);
19592 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19594 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19596 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19597 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19599 if(totalRows != 6){
19600 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19601 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19604 this.fireEvent('monthchange', this, date);
19608 if(!this.internalRender){
19609 var main = this.el.dom.firstChild;
19610 var w = main.offsetWidth;
19611 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19612 Roo.fly(main).setWidth(w);
19613 this.internalRender = true;
19614 // opera does not respect the auto grow header center column
19615 // then, after it gets a width opera refuses to recalculate
19616 // without a second pass
19617 if(Roo.isOpera && !this.secondPass){
19618 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19619 this.secondPass = true;
19620 this.update.defer(10, this, [date]);
19627 findCell : function(dt) {
19628 dt = dt.clearTime().getTime();
19630 this.cells.each(function(c){
19631 //Roo.log("check " +c.dateValue + '?=' + dt);
19632 if(c.dateValue == dt){
19642 findCells : function(ev) {
19643 var s = ev.start.clone().clearTime().getTime();
19645 var e= ev.end.clone().clearTime().getTime();
19648 this.cells.each(function(c){
19649 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19651 if(c.dateValue > e){
19654 if(c.dateValue < s){
19663 // findBestRow: function(cells)
19667 // for (var i =0 ; i < cells.length;i++) {
19668 // ret = Math.max(cells[i].rows || 0,ret);
19675 addItem : function(ev)
19677 // look for vertical location slot in
19678 var cells = this.findCells(ev);
19680 // ev.row = this.findBestRow(cells);
19682 // work out the location.
19686 for(var i =0; i < cells.length; i++) {
19688 cells[i].row = cells[0].row;
19691 cells[i].row = cells[i].row + 1;
19701 if (crow.start.getY() == cells[i].getY()) {
19703 crow.end = cells[i];
19720 cells[0].events.push(ev);
19722 this.calevents.push(ev);
19725 clearEvents: function() {
19727 if(!this.calevents){
19731 Roo.each(this.cells.elements, function(c){
19737 Roo.each(this.calevents, function(e) {
19738 Roo.each(e.els, function(el) {
19739 el.un('mouseenter' ,this.onEventEnter, this);
19740 el.un('mouseleave' ,this.onEventLeave, this);
19745 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19751 renderEvents: function()
19755 this.cells.each(function(c) {
19764 if(c.row != c.events.length){
19765 r = 4 - (4 - (c.row - c.events.length));
19768 c.events = ev.slice(0, r);
19769 c.more = ev.slice(r);
19771 if(c.more.length && c.more.length == 1){
19772 c.events.push(c.more.pop());
19775 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19779 this.cells.each(function(c) {
19781 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19784 for (var e = 0; e < c.events.length; e++){
19785 var ev = c.events[e];
19786 var rows = ev.rows;
19788 for(var i = 0; i < rows.length; i++) {
19790 // how many rows should it span..
19793 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19794 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19796 unselectable : "on",
19799 cls: 'fc-event-inner',
19803 // cls: 'fc-event-time',
19804 // html : cells.length > 1 ? '' : ev.time
19808 cls: 'fc-event-title',
19809 html : String.format('{0}', ev.title)
19816 cls: 'ui-resizable-handle ui-resizable-e',
19817 html : '  '
19824 cfg.cls += ' fc-event-start';
19826 if ((i+1) == rows.length) {
19827 cfg.cls += ' fc-event-end';
19830 var ctr = _this.el.select('.fc-event-container',true).first();
19831 var cg = ctr.createChild(cfg);
19833 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19834 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19836 var r = (c.more.length) ? 1 : 0;
19837 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19838 cg.setWidth(ebox.right - sbox.x -2);
19840 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19841 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19842 cg.on('click', _this.onEventClick, _this, ev);
19853 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19854 style : 'position: absolute',
19855 unselectable : "on",
19858 cls: 'fc-event-inner',
19862 cls: 'fc-event-title',
19870 cls: 'ui-resizable-handle ui-resizable-e',
19871 html : '  '
19877 var ctr = _this.el.select('.fc-event-container',true).first();
19878 var cg = ctr.createChild(cfg);
19880 var sbox = c.select('.fc-day-content',true).first().getBox();
19881 var ebox = c.select('.fc-day-content',true).first().getBox();
19883 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19884 cg.setWidth(ebox.right - sbox.x -2);
19886 cg.on('click', _this.onMoreEventClick, _this, c.more);
19896 onEventEnter: function (e, el,event,d) {
19897 this.fireEvent('evententer', this, el, event);
19900 onEventLeave: function (e, el,event,d) {
19901 this.fireEvent('eventleave', this, el, event);
19904 onEventClick: function (e, el,event,d) {
19905 this.fireEvent('eventclick', this, el, event);
19908 onMonthChange: function () {
19912 onMoreEventClick: function(e, el, more)
19916 this.calpopover.placement = 'right';
19917 this.calpopover.setTitle('More');
19919 this.calpopover.setContent('');
19921 var ctr = this.calpopover.el.select('.popover-content', true).first();
19923 Roo.each(more, function(m){
19925 cls : 'fc-event-hori fc-event-draggable',
19928 var cg = ctr.createChild(cfg);
19930 cg.on('click', _this.onEventClick, _this, m);
19933 this.calpopover.show(el);
19938 onLoad: function ()
19940 this.calevents = [];
19943 if(this.store.getCount() > 0){
19944 this.store.data.each(function(d){
19947 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19948 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19949 time : d.data.start_time,
19950 title : d.data.title,
19951 description : d.data.description,
19952 venue : d.data.venue
19957 this.renderEvents();
19959 if(this.calevents.length && this.loadMask){
19960 this.maskEl.hide();
19964 onBeforeLoad: function()
19966 this.clearEvents();
19968 this.maskEl.show();
19982 * @class Roo.bootstrap.Popover
19983 * @extends Roo.bootstrap.Component
19984 * Bootstrap Popover class
19985 * @cfg {String} html contents of the popover (or false to use children..)
19986 * @cfg {String} title of popover (or false to hide)
19987 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19988 * @cfg {String} trigger click || hover (or false to trigger manually)
19989 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19990 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19991 * - if false and it has a 'parent' then it will be automatically added to that element
19992 * - if string - Roo.get will be called
19993 * @cfg {Number} delay - delay before showing
19996 * Create a new Popover
19997 * @param {Object} config The config object
20000 Roo.bootstrap.Popover = function(config){
20001 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20007 * After the popover show
20009 * @param {Roo.bootstrap.Popover} this
20014 * After the popover hide
20016 * @param {Roo.bootstrap.Popover} this
20022 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20027 placement : 'right',
20028 trigger : 'hover', // hover
20034 can_build_overlaid : false,
20036 maskEl : false, // the mask element
20039 alignEl : false, // when show is called with an element - this get's stored.
20041 getChildContainer : function()
20043 return this.contentEl;
20046 getPopoverHeader : function()
20048 this.title = true; // flag not to hide it..
20049 this.headerEl.addClass('p-0');
20050 return this.headerEl
20054 getAutoCreate : function(){
20057 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20058 style: 'display:block',
20064 cls : 'popover-inner ',
20068 cls: 'popover-title popover-header',
20069 html : this.title === false ? '' : this.title
20072 cls : 'popover-content popover-body ' + (this.cls || ''),
20073 html : this.html || ''
20084 * @param {string} the title
20086 setTitle: function(str)
20090 this.headerEl.dom.innerHTML = str;
20095 * @param {string} the body content
20097 setContent: function(str)
20100 if (this.contentEl) {
20101 this.contentEl.dom.innerHTML = str;
20105 // as it get's added to the bottom of the page.
20106 onRender : function(ct, position)
20108 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20113 var cfg = Roo.apply({}, this.getAutoCreate());
20117 cfg.cls += ' ' + this.cls;
20120 cfg.style = this.style;
20122 //Roo.log("adding to ");
20123 this.el = Roo.get(document.body).createChild(cfg, position);
20124 // Roo.log(this.el);
20127 this.contentEl = this.el.select('.popover-content',true).first();
20128 this.headerEl = this.el.select('.popover-title',true).first();
20131 if(typeof(this.items) != 'undefined'){
20132 var items = this.items;
20135 for(var i =0;i < items.length;i++) {
20136 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20140 this.items = nitems;
20142 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20143 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20150 resizeMask : function()
20152 this.maskEl.setSize(
20153 Roo.lib.Dom.getViewWidth(true),
20154 Roo.lib.Dom.getViewHeight(true)
20158 initEvents : function()
20162 Roo.bootstrap.Popover.register(this);
20165 this.arrowEl = this.el.select('.arrow',true).first();
20166 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20167 this.el.enableDisplayMode('block');
20171 if (this.over === false && !this.parent()) {
20174 if (this.triggers === false) {
20179 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20180 var triggers = this.trigger ? this.trigger.split(' ') : [];
20181 Roo.each(triggers, function(trigger) {
20183 if (trigger == 'click') {
20184 on_el.on('click', this.toggle, this);
20185 } else if (trigger != 'manual') {
20186 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20187 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20189 on_el.on(eventIn ,this.enter, this);
20190 on_el.on(eventOut, this.leave, this);
20200 toggle : function () {
20201 this.hoverState == 'in' ? this.leave() : this.enter();
20204 enter : function () {
20206 clearTimeout(this.timeout);
20208 this.hoverState = 'in';
20210 if (!this.delay || !this.delay.show) {
20215 this.timeout = setTimeout(function () {
20216 if (_t.hoverState == 'in') {
20219 }, this.delay.show)
20222 leave : function() {
20223 clearTimeout(this.timeout);
20225 this.hoverState = 'out';
20227 if (!this.delay || !this.delay.hide) {
20232 this.timeout = setTimeout(function () {
20233 if (_t.hoverState == 'out') {
20236 }, this.delay.hide)
20240 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20241 * @param {string} (left|right|top|bottom) position
20243 show : function (on_el, placement)
20245 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20246 on_el = on_el || false; // default to false
20249 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20250 on_el = this.parent().el;
20251 } else if (this.over) {
20252 on_el = Roo.get(this.over);
20257 this.alignEl = Roo.get( on_el );
20260 this.render(document.body);
20266 if (this.title === false) {
20267 this.headerEl.hide();
20272 this.el.dom.style.display = 'block';
20275 if (this.alignEl) {
20276 this.updatePosition(this.placement, true);
20279 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20280 var es = this.el.getSize();
20281 var x = Roo.lib.Dom.getViewWidth()/2;
20282 var y = Roo.lib.Dom.getViewHeight()/2;
20283 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20288 //var arrow = this.el.select('.arrow',true).first();
20289 //arrow.set(align[2],
20291 this.el.addClass('in');
20295 this.hoverState = 'in';
20298 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20299 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20300 this.maskEl.dom.style.display = 'block';
20301 this.maskEl.addClass('show');
20303 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20305 this.fireEvent('show', this);
20309 * fire this manually after loading a grid in the table for example
20310 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20311 * @param {Boolean} try and move it if we cant get right position.
20313 updatePosition : function(placement, try_move)
20315 // allow for calling with no parameters
20316 placement = placement ? placement : this.placement;
20317 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20319 this.el.removeClass([
20320 'fade','top','bottom', 'left', 'right','in',
20321 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20323 this.el.addClass(placement + ' bs-popover-' + placement);
20325 if (!this.alignEl ) {
20329 switch (placement) {
20331 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20332 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20333 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20334 //normal display... or moved up/down.
20335 this.el.setXY(offset);
20336 var xy = this.alignEl.getAnchorXY('tr', false);
20338 this.arrowEl.setXY(xy);
20341 // continue through...
20342 return this.updatePosition('left', false);
20346 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20347 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20348 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20349 //normal display... or moved up/down.
20350 this.el.setXY(offset);
20351 var xy = this.alignEl.getAnchorXY('tl', false);
20352 xy[0]-=10;xy[1]+=5; // << fix me
20353 this.arrowEl.setXY(xy);
20357 return this.updatePosition('right', false);
20360 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20361 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20362 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20363 //normal display... or moved up/down.
20364 this.el.setXY(offset);
20365 var xy = this.alignEl.getAnchorXY('t', false);
20366 xy[1]-=10; // << fix me
20367 this.arrowEl.setXY(xy);
20371 return this.updatePosition('bottom', false);
20374 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20375 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20376 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20377 //normal display... or moved up/down.
20378 this.el.setXY(offset);
20379 var xy = this.alignEl.getAnchorXY('b', false);
20380 xy[1]+=2; // << fix me
20381 this.arrowEl.setXY(xy);
20385 return this.updatePosition('top', false);
20396 this.el.setXY([0,0]);
20397 this.el.removeClass('in');
20399 this.hoverState = null;
20400 this.maskEl.hide(); // always..
20401 this.fireEvent('hide', this);
20407 Roo.apply(Roo.bootstrap.Popover, {
20410 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20411 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20412 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20413 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20418 clickHander : false,
20422 onMouseDown : function(e)
20424 if (this.popups.length && !e.getTarget(".roo-popover")) {
20425 /// what is nothing is showing..
20434 register : function(popup)
20436 if (!Roo.bootstrap.Popover.clickHandler) {
20437 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20439 // hide other popups.
20440 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
20441 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
20442 this.hideAll(); //<< why?
20443 //this.popups.push(popup);
20445 hideAll : function()
20447 this.popups.forEach(function(p) {
20451 onShow : function() {
20452 Roo.bootstrap.Popover.popups.push(this);
20454 onHide : function() {
20455 Roo.bootstrap.Popover.popups.remove(this);
20461 * Card header - holder for the card header elements.
20466 * @class Roo.bootstrap.PopoverNav
20467 * @extends Roo.bootstrap.NavGroup
20468 * Bootstrap Popover header navigation class
20470 * Create a new Popover Header Navigation
20471 * @param {Object} config The config object
20474 Roo.bootstrap.PopoverNav = function(config){
20475 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20478 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20481 container_method : 'getPopoverHeader'
20499 * @class Roo.bootstrap.Progress
20500 * @extends Roo.bootstrap.Component
20501 * Bootstrap Progress class
20502 * @cfg {Boolean} striped striped of the progress bar
20503 * @cfg {Boolean} active animated of the progress bar
20507 * Create a new Progress
20508 * @param {Object} config The config object
20511 Roo.bootstrap.Progress = function(config){
20512 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20515 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20520 getAutoCreate : function(){
20528 cfg.cls += ' progress-striped';
20532 cfg.cls += ' active';
20551 * @class Roo.bootstrap.ProgressBar
20552 * @extends Roo.bootstrap.Component
20553 * Bootstrap ProgressBar class
20554 * @cfg {Number} aria_valuenow aria-value now
20555 * @cfg {Number} aria_valuemin aria-value min
20556 * @cfg {Number} aria_valuemax aria-value max
20557 * @cfg {String} label label for the progress bar
20558 * @cfg {String} panel (success | info | warning | danger )
20559 * @cfg {String} role role of the progress bar
20560 * @cfg {String} sr_only text
20564 * Create a new ProgressBar
20565 * @param {Object} config The config object
20568 Roo.bootstrap.ProgressBar = function(config){
20569 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20572 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20576 aria_valuemax : 100,
20582 getAutoCreate : function()
20587 cls: 'progress-bar',
20588 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20600 cfg.role = this.role;
20603 if(this.aria_valuenow){
20604 cfg['aria-valuenow'] = this.aria_valuenow;
20607 if(this.aria_valuemin){
20608 cfg['aria-valuemin'] = this.aria_valuemin;
20611 if(this.aria_valuemax){
20612 cfg['aria-valuemax'] = this.aria_valuemax;
20615 if(this.label && !this.sr_only){
20616 cfg.html = this.label;
20620 cfg.cls += ' progress-bar-' + this.panel;
20626 update : function(aria_valuenow)
20628 this.aria_valuenow = aria_valuenow;
20630 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20645 * @class Roo.bootstrap.TabGroup
20646 * @extends Roo.bootstrap.Column
20647 * Bootstrap Column class
20648 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20649 * @cfg {Boolean} carousel true to make the group behave like a carousel
20650 * @cfg {Boolean} bullets show bullets for the panels
20651 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20652 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20653 * @cfg {Boolean} showarrow (true|false) show arrow default true
20656 * Create a new TabGroup
20657 * @param {Object} config The config object
20660 Roo.bootstrap.TabGroup = function(config){
20661 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20663 this.navId = Roo.id();
20666 Roo.bootstrap.TabGroup.register(this);
20670 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20673 transition : false,
20678 slideOnTouch : false,
20681 getAutoCreate : function()
20683 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20685 cfg.cls += ' tab-content';
20687 if (this.carousel) {
20688 cfg.cls += ' carousel slide';
20691 cls : 'carousel-inner',
20695 if(this.bullets && !Roo.isTouch){
20698 cls : 'carousel-bullets',
20702 if(this.bullets_cls){
20703 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20710 cfg.cn[0].cn.push(bullets);
20713 if(this.showarrow){
20714 cfg.cn[0].cn.push({
20716 class : 'carousel-arrow',
20720 class : 'carousel-prev',
20724 class : 'fa fa-chevron-left'
20730 class : 'carousel-next',
20734 class : 'fa fa-chevron-right'
20747 initEvents: function()
20749 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20750 // this.el.on("touchstart", this.onTouchStart, this);
20753 if(this.autoslide){
20756 this.slideFn = window.setInterval(function() {
20757 _this.showPanelNext();
20761 if(this.showarrow){
20762 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20763 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20769 // onTouchStart : function(e, el, o)
20771 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20775 // this.showPanelNext();
20779 getChildContainer : function()
20781 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20785 * register a Navigation item
20786 * @param {Roo.bootstrap.NavItem} the navitem to add
20788 register : function(item)
20790 this.tabs.push( item);
20791 item.navId = this.navId; // not really needed..
20796 getActivePanel : function()
20799 Roo.each(this.tabs, function(t) {
20809 getPanelByName : function(n)
20812 Roo.each(this.tabs, function(t) {
20813 if (t.tabId == n) {
20821 indexOfPanel : function(p)
20824 Roo.each(this.tabs, function(t,i) {
20825 if (t.tabId == p.tabId) {
20834 * show a specific panel
20835 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20836 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20838 showPanel : function (pan)
20840 if(this.transition || typeof(pan) == 'undefined'){
20841 Roo.log("waiting for the transitionend");
20845 if (typeof(pan) == 'number') {
20846 pan = this.tabs[pan];
20849 if (typeof(pan) == 'string') {
20850 pan = this.getPanelByName(pan);
20853 var cur = this.getActivePanel();
20856 Roo.log('pan or acitve pan is undefined');
20860 if (pan.tabId == this.getActivePanel().tabId) {
20864 if (false === cur.fireEvent('beforedeactivate')) {
20868 if(this.bullets > 0 && !Roo.isTouch){
20869 this.setActiveBullet(this.indexOfPanel(pan));
20872 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20874 //class="carousel-item carousel-item-next carousel-item-left"
20876 this.transition = true;
20877 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20878 var lr = dir == 'next' ? 'left' : 'right';
20879 pan.el.addClass(dir); // or prev
20880 pan.el.addClass('carousel-item-' + dir); // or prev
20881 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20882 cur.el.addClass(lr); // or right
20883 pan.el.addClass(lr);
20884 cur.el.addClass('carousel-item-' +lr); // or right
20885 pan.el.addClass('carousel-item-' +lr);
20889 cur.el.on('transitionend', function() {
20890 Roo.log("trans end?");
20892 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20893 pan.setActive(true);
20895 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20896 cur.setActive(false);
20898 _this.transition = false;
20900 }, this, { single: true } );
20905 cur.setActive(false);
20906 pan.setActive(true);
20911 showPanelNext : function()
20913 var i = this.indexOfPanel(this.getActivePanel());
20915 if (i >= this.tabs.length - 1 && !this.autoslide) {
20919 if (i >= this.tabs.length - 1 && this.autoslide) {
20923 this.showPanel(this.tabs[i+1]);
20926 showPanelPrev : function()
20928 var i = this.indexOfPanel(this.getActivePanel());
20930 if (i < 1 && !this.autoslide) {
20934 if (i < 1 && this.autoslide) {
20935 i = this.tabs.length;
20938 this.showPanel(this.tabs[i-1]);
20942 addBullet: function()
20944 if(!this.bullets || Roo.isTouch){
20947 var ctr = this.el.select('.carousel-bullets',true).first();
20948 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20949 var bullet = ctr.createChild({
20950 cls : 'bullet bullet-' + i
20951 },ctr.dom.lastChild);
20956 bullet.on('click', (function(e, el, o, ii, t){
20958 e.preventDefault();
20960 this.showPanel(ii);
20962 if(this.autoslide && this.slideFn){
20963 clearInterval(this.slideFn);
20964 this.slideFn = window.setInterval(function() {
20965 _this.showPanelNext();
20969 }).createDelegate(this, [i, bullet], true));
20974 setActiveBullet : function(i)
20980 Roo.each(this.el.select('.bullet', true).elements, function(el){
20981 el.removeClass('selected');
20984 var bullet = this.el.select('.bullet-' + i, true).first();
20990 bullet.addClass('selected');
21001 Roo.apply(Roo.bootstrap.TabGroup, {
21005 * register a Navigation Group
21006 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21008 register : function(navgrp)
21010 this.groups[navgrp.navId] = navgrp;
21014 * fetch a Navigation Group based on the navigation ID
21015 * if one does not exist , it will get created.
21016 * @param {string} the navgroup to add
21017 * @returns {Roo.bootstrap.NavGroup} the navgroup
21019 get: function(navId) {
21020 if (typeof(this.groups[navId]) == 'undefined') {
21021 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21023 return this.groups[navId] ;
21038 * @class Roo.bootstrap.TabPanel
21039 * @extends Roo.bootstrap.Component
21040 * Bootstrap TabPanel class
21041 * @cfg {Boolean} active panel active
21042 * @cfg {String} html panel content
21043 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21044 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21045 * @cfg {String} href click to link..
21046 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21050 * Create a new TabPanel
21051 * @param {Object} config The config object
21054 Roo.bootstrap.TabPanel = function(config){
21055 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21059 * Fires when the active status changes
21060 * @param {Roo.bootstrap.TabPanel} this
21061 * @param {Boolean} state the new state
21066 * @event beforedeactivate
21067 * Fires before a tab is de-activated - can be used to do validation on a form.
21068 * @param {Roo.bootstrap.TabPanel} this
21069 * @return {Boolean} false if there is an error
21072 'beforedeactivate': true
21075 this.tabId = this.tabId || Roo.id();
21079 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21086 touchSlide : false,
21087 getAutoCreate : function(){
21092 // item is needed for carousel - not sure if it has any effect otherwise
21093 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21094 html: this.html || ''
21098 cfg.cls += ' active';
21102 cfg.tabId = this.tabId;
21110 initEvents: function()
21112 var p = this.parent();
21114 this.navId = this.navId || p.navId;
21116 if (typeof(this.navId) != 'undefined') {
21117 // not really needed.. but just in case.. parent should be a NavGroup.
21118 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21122 var i = tg.tabs.length - 1;
21124 if(this.active && tg.bullets > 0 && i < tg.bullets){
21125 tg.setActiveBullet(i);
21129 this.el.on('click', this.onClick, this);
21131 if(Roo.isTouch && this.touchSlide){
21132 this.el.on("touchstart", this.onTouchStart, this);
21133 this.el.on("touchmove", this.onTouchMove, this);
21134 this.el.on("touchend", this.onTouchEnd, this);
21139 onRender : function(ct, position)
21141 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21144 setActive : function(state)
21146 Roo.log("panel - set active " + this.tabId + "=" + state);
21148 this.active = state;
21150 this.el.removeClass('active');
21152 } else if (!this.el.hasClass('active')) {
21153 this.el.addClass('active');
21156 this.fireEvent('changed', this, state);
21159 onClick : function(e)
21161 e.preventDefault();
21163 if(!this.href.length){
21167 window.location.href = this.href;
21176 onTouchStart : function(e)
21178 this.swiping = false;
21180 this.startX = e.browserEvent.touches[0].clientX;
21181 this.startY = e.browserEvent.touches[0].clientY;
21184 onTouchMove : function(e)
21186 this.swiping = true;
21188 this.endX = e.browserEvent.touches[0].clientX;
21189 this.endY = e.browserEvent.touches[0].clientY;
21192 onTouchEnd : function(e)
21199 var tabGroup = this.parent();
21201 if(this.endX > this.startX){ // swiping right
21202 tabGroup.showPanelPrev();
21206 if(this.startX > this.endX){ // swiping left
21207 tabGroup.showPanelNext();
21226 * @class Roo.bootstrap.DateField
21227 * @extends Roo.bootstrap.Input
21228 * Bootstrap DateField class
21229 * @cfg {Number} weekStart default 0
21230 * @cfg {String} viewMode default empty, (months|years)
21231 * @cfg {String} minViewMode default empty, (months|years)
21232 * @cfg {Number} startDate default -Infinity
21233 * @cfg {Number} endDate default Infinity
21234 * @cfg {Boolean} todayHighlight default false
21235 * @cfg {Boolean} todayBtn default false
21236 * @cfg {Boolean} calendarWeeks default false
21237 * @cfg {Object} daysOfWeekDisabled default empty
21238 * @cfg {Boolean} singleMode default false (true | false)
21240 * @cfg {Boolean} keyboardNavigation default true
21241 * @cfg {String} language default en
21244 * Create a new DateField
21245 * @param {Object} config The config object
21248 Roo.bootstrap.DateField = function(config){
21249 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21253 * Fires when this field show.
21254 * @param {Roo.bootstrap.DateField} this
21255 * @param {Mixed} date The date value
21260 * Fires when this field hide.
21261 * @param {Roo.bootstrap.DateField} this
21262 * @param {Mixed} date The date value
21267 * Fires when select a date.
21268 * @param {Roo.bootstrap.DateField} this
21269 * @param {Mixed} date The date value
21273 * @event beforeselect
21274 * Fires when before select a date.
21275 * @param {Roo.bootstrap.DateField} this
21276 * @param {Mixed} date The date value
21278 beforeselect : true
21282 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21285 * @cfg {String} format
21286 * The default date format string which can be overriden for localization support. The format must be
21287 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21291 * @cfg {String} altFormats
21292 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21293 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21295 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21303 todayHighlight : false,
21309 keyboardNavigation: true,
21311 calendarWeeks: false,
21313 startDate: -Infinity,
21317 daysOfWeekDisabled: [],
21321 singleMode : false,
21323 UTCDate: function()
21325 return new Date(Date.UTC.apply(Date, arguments));
21328 UTCToday: function()
21330 var today = new Date();
21331 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21334 getDate: function() {
21335 var d = this.getUTCDate();
21336 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21339 getUTCDate: function() {
21343 setDate: function(d) {
21344 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21347 setUTCDate: function(d) {
21349 this.setValue(this.formatDate(this.date));
21352 onRender: function(ct, position)
21355 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21357 this.language = this.language || 'en';
21358 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21359 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21361 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21362 this.format = this.format || 'm/d/y';
21363 this.isInline = false;
21364 this.isInput = true;
21365 this.component = this.el.select('.add-on', true).first() || false;
21366 this.component = (this.component && this.component.length === 0) ? false : this.component;
21367 this.hasInput = this.component && this.inputEl().length;
21369 if (typeof(this.minViewMode === 'string')) {
21370 switch (this.minViewMode) {
21372 this.minViewMode = 1;
21375 this.minViewMode = 2;
21378 this.minViewMode = 0;
21383 if (typeof(this.viewMode === 'string')) {
21384 switch (this.viewMode) {
21397 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21399 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21401 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21403 this.picker().on('mousedown', this.onMousedown, this);
21404 this.picker().on('click', this.onClick, this);
21406 this.picker().addClass('datepicker-dropdown');
21408 this.startViewMode = this.viewMode;
21410 if(this.singleMode){
21411 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21412 v.setVisibilityMode(Roo.Element.DISPLAY);
21416 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21417 v.setStyle('width', '189px');
21421 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21422 if(!this.calendarWeeks){
21427 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21428 v.attr('colspan', function(i, val){
21429 return parseInt(val) + 1;
21434 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21436 this.setStartDate(this.startDate);
21437 this.setEndDate(this.endDate);
21439 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21446 if(this.isInline) {
21451 picker : function()
21453 return this.pickerEl;
21454 // return this.el.select('.datepicker', true).first();
21457 fillDow: function()
21459 var dowCnt = this.weekStart;
21468 if(this.calendarWeeks){
21476 while (dowCnt < this.weekStart + 7) {
21480 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21484 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21487 fillMonths: function()
21490 var months = this.picker().select('>.datepicker-months td', true).first();
21492 months.dom.innerHTML = '';
21498 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21501 months.createChild(month);
21508 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;
21510 if (this.date < this.startDate) {
21511 this.viewDate = new Date(this.startDate);
21512 } else if (this.date > this.endDate) {
21513 this.viewDate = new Date(this.endDate);
21515 this.viewDate = new Date(this.date);
21523 var d = new Date(this.viewDate),
21524 year = d.getUTCFullYear(),
21525 month = d.getUTCMonth(),
21526 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21527 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21528 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21529 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21530 currentDate = this.date && this.date.valueOf(),
21531 today = this.UTCToday();
21533 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21535 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21537 // this.picker.select('>tfoot th.today').
21538 // .text(dates[this.language].today)
21539 // .toggle(this.todayBtn !== false);
21541 this.updateNavArrows();
21544 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21546 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21548 prevMonth.setUTCDate(day);
21550 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21552 var nextMonth = new Date(prevMonth);
21554 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21556 nextMonth = nextMonth.valueOf();
21558 var fillMonths = false;
21560 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21562 while(prevMonth.valueOf() <= nextMonth) {
21565 if (prevMonth.getUTCDay() === this.weekStart) {
21567 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21575 if(this.calendarWeeks){
21576 // ISO 8601: First week contains first thursday.
21577 // ISO also states week starts on Monday, but we can be more abstract here.
21579 // Start of current week: based on weekstart/current date
21580 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21581 // Thursday of this week
21582 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21583 // First Thursday of year, year from thursday
21584 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21585 // Calendar week: ms between thursdays, div ms per day, div 7 days
21586 calWeek = (th - yth) / 864e5 / 7 + 1;
21588 fillMonths.cn.push({
21596 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21598 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21601 if (this.todayHighlight &&
21602 prevMonth.getUTCFullYear() == today.getFullYear() &&
21603 prevMonth.getUTCMonth() == today.getMonth() &&
21604 prevMonth.getUTCDate() == today.getDate()) {
21605 clsName += ' today';
21608 if (currentDate && prevMonth.valueOf() === currentDate) {
21609 clsName += ' active';
21612 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21613 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21614 clsName += ' disabled';
21617 fillMonths.cn.push({
21619 cls: 'day ' + clsName,
21620 html: prevMonth.getDate()
21623 prevMonth.setDate(prevMonth.getDate()+1);
21626 var currentYear = this.date && this.date.getUTCFullYear();
21627 var currentMonth = this.date && this.date.getUTCMonth();
21629 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21631 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21632 v.removeClass('active');
21634 if(currentYear === year && k === currentMonth){
21635 v.addClass('active');
21638 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21639 v.addClass('disabled');
21645 year = parseInt(year/10, 10) * 10;
21647 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21649 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21652 for (var i = -1; i < 11; i++) {
21653 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21655 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21663 showMode: function(dir)
21666 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21669 Roo.each(this.picker().select('>div',true).elements, function(v){
21670 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21673 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21678 if(this.isInline) {
21682 this.picker().removeClass(['bottom', 'top']);
21684 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21686 * place to the top of element!
21690 this.picker().addClass('top');
21691 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21696 this.picker().addClass('bottom');
21698 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21701 parseDate : function(value)
21703 if(!value || value instanceof Date){
21706 var v = Date.parseDate(value, this.format);
21707 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21708 v = Date.parseDate(value, 'Y-m-d');
21710 if(!v && this.altFormats){
21711 if(!this.altFormatsArray){
21712 this.altFormatsArray = this.altFormats.split("|");
21714 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21715 v = Date.parseDate(value, this.altFormatsArray[i]);
21721 formatDate : function(date, fmt)
21723 return (!date || !(date instanceof Date)) ?
21724 date : date.dateFormat(fmt || this.format);
21727 onFocus : function()
21729 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21733 onBlur : function()
21735 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21737 var d = this.inputEl().getValue();
21744 showPopup : function()
21746 this.picker().show();
21750 this.fireEvent('showpopup', this, this.date);
21753 hidePopup : function()
21755 if(this.isInline) {
21758 this.picker().hide();
21759 this.viewMode = this.startViewMode;
21762 this.fireEvent('hidepopup', this, this.date);
21766 onMousedown: function(e)
21768 e.stopPropagation();
21769 e.preventDefault();
21774 Roo.bootstrap.DateField.superclass.keyup.call(this);
21778 setValue: function(v)
21780 if(this.fireEvent('beforeselect', this, v) !== false){
21781 var d = new Date(this.parseDate(v) ).clearTime();
21783 if(isNaN(d.getTime())){
21784 this.date = this.viewDate = '';
21785 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21789 v = this.formatDate(d);
21791 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21793 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21797 this.fireEvent('select', this, this.date);
21801 getValue: function()
21803 return this.formatDate(this.date);
21806 fireKey: function(e)
21808 if (!this.picker().isVisible()){
21809 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21815 var dateChanged = false,
21817 newDate, newViewDate;
21822 e.preventDefault();
21826 if (!this.keyboardNavigation) {
21829 dir = e.keyCode == 37 ? -1 : 1;
21832 newDate = this.moveYear(this.date, dir);
21833 newViewDate = this.moveYear(this.viewDate, dir);
21834 } else if (e.shiftKey){
21835 newDate = this.moveMonth(this.date, dir);
21836 newViewDate = this.moveMonth(this.viewDate, dir);
21838 newDate = new Date(this.date);
21839 newDate.setUTCDate(this.date.getUTCDate() + dir);
21840 newViewDate = new Date(this.viewDate);
21841 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21843 if (this.dateWithinRange(newDate)){
21844 this.date = newDate;
21845 this.viewDate = newViewDate;
21846 this.setValue(this.formatDate(this.date));
21848 e.preventDefault();
21849 dateChanged = true;
21854 if (!this.keyboardNavigation) {
21857 dir = e.keyCode == 38 ? -1 : 1;
21859 newDate = this.moveYear(this.date, dir);
21860 newViewDate = this.moveYear(this.viewDate, dir);
21861 } else if (e.shiftKey){
21862 newDate = this.moveMonth(this.date, dir);
21863 newViewDate = this.moveMonth(this.viewDate, dir);
21865 newDate = new Date(this.date);
21866 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21867 newViewDate = new Date(this.viewDate);
21868 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21870 if (this.dateWithinRange(newDate)){
21871 this.date = newDate;
21872 this.viewDate = newViewDate;
21873 this.setValue(this.formatDate(this.date));
21875 e.preventDefault();
21876 dateChanged = true;
21880 this.setValue(this.formatDate(this.date));
21882 e.preventDefault();
21885 this.setValue(this.formatDate(this.date));
21899 onClick: function(e)
21901 e.stopPropagation();
21902 e.preventDefault();
21904 var target = e.getTarget();
21906 if(target.nodeName.toLowerCase() === 'i'){
21907 target = Roo.get(target).dom.parentNode;
21910 var nodeName = target.nodeName;
21911 var className = target.className;
21912 var html = target.innerHTML;
21913 //Roo.log(nodeName);
21915 switch(nodeName.toLowerCase()) {
21917 switch(className) {
21923 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21924 switch(this.viewMode){
21926 this.viewDate = this.moveMonth(this.viewDate, dir);
21930 this.viewDate = this.moveYear(this.viewDate, dir);
21936 var date = new Date();
21937 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21939 this.setValue(this.formatDate(this.date));
21946 if (className.indexOf('disabled') < 0) {
21947 this.viewDate.setUTCDate(1);
21948 if (className.indexOf('month') > -1) {
21949 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21951 var year = parseInt(html, 10) || 0;
21952 this.viewDate.setUTCFullYear(year);
21956 if(this.singleMode){
21957 this.setValue(this.formatDate(this.viewDate));
21968 //Roo.log(className);
21969 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21970 var day = parseInt(html, 10) || 1;
21971 var year = (this.viewDate || new Date()).getUTCFullYear(),
21972 month = (this.viewDate || new Date()).getUTCMonth();
21974 if (className.indexOf('old') > -1) {
21981 } else if (className.indexOf('new') > -1) {
21989 //Roo.log([year,month,day]);
21990 this.date = this.UTCDate(year, month, day,0,0,0,0);
21991 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21993 //Roo.log(this.formatDate(this.date));
21994 this.setValue(this.formatDate(this.date));
22001 setStartDate: function(startDate)
22003 this.startDate = startDate || -Infinity;
22004 if (this.startDate !== -Infinity) {
22005 this.startDate = this.parseDate(this.startDate);
22008 this.updateNavArrows();
22011 setEndDate: function(endDate)
22013 this.endDate = endDate || Infinity;
22014 if (this.endDate !== Infinity) {
22015 this.endDate = this.parseDate(this.endDate);
22018 this.updateNavArrows();
22021 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22023 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22024 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22025 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22027 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22028 return parseInt(d, 10);
22031 this.updateNavArrows();
22034 updateNavArrows: function()
22036 if(this.singleMode){
22040 var d = new Date(this.viewDate),
22041 year = d.getUTCFullYear(),
22042 month = d.getUTCMonth();
22044 Roo.each(this.picker().select('.prev', true).elements, function(v){
22046 switch (this.viewMode) {
22049 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22055 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22062 Roo.each(this.picker().select('.next', true).elements, function(v){
22064 switch (this.viewMode) {
22067 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22073 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22081 moveMonth: function(date, dir)
22086 var new_date = new Date(date.valueOf()),
22087 day = new_date.getUTCDate(),
22088 month = new_date.getUTCMonth(),
22089 mag = Math.abs(dir),
22091 dir = dir > 0 ? 1 : -1;
22094 // If going back one month, make sure month is not current month
22095 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22097 return new_date.getUTCMonth() == month;
22099 // If going forward one month, make sure month is as expected
22100 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22102 return new_date.getUTCMonth() != new_month;
22104 new_month = month + dir;
22105 new_date.setUTCMonth(new_month);
22106 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22107 if (new_month < 0 || new_month > 11) {
22108 new_month = (new_month + 12) % 12;
22111 // For magnitudes >1, move one month at a time...
22112 for (var i=0; i<mag; i++) {
22113 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22114 new_date = this.moveMonth(new_date, dir);
22116 // ...then reset the day, keeping it in the new month
22117 new_month = new_date.getUTCMonth();
22118 new_date.setUTCDate(day);
22120 return new_month != new_date.getUTCMonth();
22123 // Common date-resetting loop -- if date is beyond end of month, make it
22126 new_date.setUTCDate(--day);
22127 new_date.setUTCMonth(new_month);
22132 moveYear: function(date, dir)
22134 return this.moveMonth(date, dir*12);
22137 dateWithinRange: function(date)
22139 return date >= this.startDate && date <= this.endDate;
22145 this.picker().remove();
22148 validateValue : function(value)
22150 if(this.getVisibilityEl().hasClass('hidden')){
22154 if(value.length < 1) {
22155 if(this.allowBlank){
22161 if(value.length < this.minLength){
22164 if(value.length > this.maxLength){
22168 var vt = Roo.form.VTypes;
22169 if(!vt[this.vtype](value, this)){
22173 if(typeof this.validator == "function"){
22174 var msg = this.validator(value);
22180 if(this.regex && !this.regex.test(value)){
22184 if(typeof(this.parseDate(value)) == 'undefined'){
22188 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22192 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22202 this.date = this.viewDate = '';
22204 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22209 Roo.apply(Roo.bootstrap.DateField, {
22220 html: '<i class="fa fa-arrow-left"/>'
22230 html: '<i class="fa fa-arrow-right"/>'
22272 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22273 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22274 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22275 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22276 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22289 navFnc: 'FullYear',
22294 navFnc: 'FullYear',
22299 Roo.apply(Roo.bootstrap.DateField, {
22303 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22307 cls: 'datepicker-days',
22311 cls: 'table-condensed',
22313 Roo.bootstrap.DateField.head,
22317 Roo.bootstrap.DateField.footer
22324 cls: 'datepicker-months',
22328 cls: 'table-condensed',
22330 Roo.bootstrap.DateField.head,
22331 Roo.bootstrap.DateField.content,
22332 Roo.bootstrap.DateField.footer
22339 cls: 'datepicker-years',
22343 cls: 'table-condensed',
22345 Roo.bootstrap.DateField.head,
22346 Roo.bootstrap.DateField.content,
22347 Roo.bootstrap.DateField.footer
22366 * @class Roo.bootstrap.TimeField
22367 * @extends Roo.bootstrap.Input
22368 * Bootstrap DateField class
22372 * Create a new TimeField
22373 * @param {Object} config The config object
22376 Roo.bootstrap.TimeField = function(config){
22377 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22381 * Fires when this field show.
22382 * @param {Roo.bootstrap.DateField} thisthis
22383 * @param {Mixed} date The date value
22388 * Fires when this field hide.
22389 * @param {Roo.bootstrap.DateField} this
22390 * @param {Mixed} date The date value
22395 * Fires when select a date.
22396 * @param {Roo.bootstrap.DateField} this
22397 * @param {Mixed} date The date value
22403 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22406 * @cfg {String} format
22407 * The default time format string which can be overriden for localization support. The format must be
22408 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22412 getAutoCreate : function()
22414 this.after = '<i class="fa far fa-clock"></i>';
22415 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22419 onRender: function(ct, position)
22422 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22424 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22426 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22428 this.pop = this.picker().select('>.datepicker-time',true).first();
22429 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22431 this.picker().on('mousedown', this.onMousedown, this);
22432 this.picker().on('click', this.onClick, this);
22434 this.picker().addClass('datepicker-dropdown');
22439 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22440 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22441 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22442 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22443 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22444 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22448 fireKey: function(e){
22449 if (!this.picker().isVisible()){
22450 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22456 e.preventDefault();
22464 this.onTogglePeriod();
22467 this.onIncrementMinutes();
22470 this.onDecrementMinutes();
22479 onClick: function(e) {
22480 e.stopPropagation();
22481 e.preventDefault();
22484 picker : function()
22486 return this.pickerEl;
22489 fillTime: function()
22491 var time = this.pop.select('tbody', true).first();
22493 time.dom.innerHTML = '';
22508 cls: 'hours-up fa fas fa-chevron-up'
22528 cls: 'minutes-up fa fas fa-chevron-up'
22549 cls: 'timepicker-hour',
22564 cls: 'timepicker-minute',
22579 cls: 'btn btn-primary period',
22601 cls: 'hours-down fa fas fa-chevron-down'
22621 cls: 'minutes-down fa fas fa-chevron-down'
22639 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22646 var hours = this.time.getHours();
22647 var minutes = this.time.getMinutes();
22660 hours = hours - 12;
22664 hours = '0' + hours;
22668 minutes = '0' + minutes;
22671 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22672 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22673 this.pop.select('button', true).first().dom.innerHTML = period;
22679 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22681 var cls = ['bottom'];
22683 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22690 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22694 //this.picker().setXY(20000,20000);
22695 this.picker().addClass(cls.join('-'));
22699 Roo.each(cls, function(c){
22704 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22705 //_this.picker().setTop(_this.inputEl().getHeight());
22709 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22711 //_this.picker().setTop(0 - _this.picker().getHeight());
22716 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22720 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22728 onFocus : function()
22730 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22734 onBlur : function()
22736 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22742 this.picker().show();
22747 this.fireEvent('show', this, this.date);
22752 this.picker().hide();
22755 this.fireEvent('hide', this, this.date);
22758 setTime : function()
22761 this.setValue(this.time.format(this.format));
22763 this.fireEvent('select', this, this.date);
22768 onMousedown: function(e){
22769 e.stopPropagation();
22770 e.preventDefault();
22773 onIncrementHours: function()
22775 Roo.log('onIncrementHours');
22776 this.time = this.time.add(Date.HOUR, 1);
22781 onDecrementHours: function()
22783 Roo.log('onDecrementHours');
22784 this.time = this.time.add(Date.HOUR, -1);
22788 onIncrementMinutes: function()
22790 Roo.log('onIncrementMinutes');
22791 this.time = this.time.add(Date.MINUTE, 1);
22795 onDecrementMinutes: function()
22797 Roo.log('onDecrementMinutes');
22798 this.time = this.time.add(Date.MINUTE, -1);
22802 onTogglePeriod: function()
22804 Roo.log('onTogglePeriod');
22805 this.time = this.time.add(Date.HOUR, 12);
22813 Roo.apply(Roo.bootstrap.TimeField, {
22817 cls: 'datepicker dropdown-menu',
22821 cls: 'datepicker-time',
22825 cls: 'table-condensed',
22854 cls: 'btn btn-info ok',
22882 * @class Roo.bootstrap.MonthField
22883 * @extends Roo.bootstrap.Input
22884 * Bootstrap MonthField class
22886 * @cfg {String} language default en
22889 * Create a new MonthField
22890 * @param {Object} config The config object
22893 Roo.bootstrap.MonthField = function(config){
22894 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22899 * Fires when this field show.
22900 * @param {Roo.bootstrap.MonthField} this
22901 * @param {Mixed} date The date value
22906 * Fires when this field hide.
22907 * @param {Roo.bootstrap.MonthField} this
22908 * @param {Mixed} date The date value
22913 * Fires when select a date.
22914 * @param {Roo.bootstrap.MonthField} this
22915 * @param {String} oldvalue The old value
22916 * @param {String} newvalue The new value
22922 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22924 onRender: function(ct, position)
22927 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22929 this.language = this.language || 'en';
22930 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22931 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22933 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22934 this.isInline = false;
22935 this.isInput = true;
22936 this.component = this.el.select('.add-on', true).first() || false;
22937 this.component = (this.component && this.component.length === 0) ? false : this.component;
22938 this.hasInput = this.component && this.inputEL().length;
22940 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22942 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22944 this.picker().on('mousedown', this.onMousedown, this);
22945 this.picker().on('click', this.onClick, this);
22947 this.picker().addClass('datepicker-dropdown');
22949 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22950 v.setStyle('width', '189px');
22957 if(this.isInline) {
22963 setValue: function(v, suppressEvent)
22965 var o = this.getValue();
22967 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22971 if(suppressEvent !== true){
22972 this.fireEvent('select', this, o, v);
22977 getValue: function()
22982 onClick: function(e)
22984 e.stopPropagation();
22985 e.preventDefault();
22987 var target = e.getTarget();
22989 if(target.nodeName.toLowerCase() === 'i'){
22990 target = Roo.get(target).dom.parentNode;
22993 var nodeName = target.nodeName;
22994 var className = target.className;
22995 var html = target.innerHTML;
22997 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23001 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23003 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23009 picker : function()
23011 return this.pickerEl;
23014 fillMonths: function()
23017 var months = this.picker().select('>.datepicker-months td', true).first();
23019 months.dom.innerHTML = '';
23025 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23028 months.createChild(month);
23037 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23038 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23041 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23042 e.removeClass('active');
23044 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23045 e.addClass('active');
23052 if(this.isInline) {
23056 this.picker().removeClass(['bottom', 'top']);
23058 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23060 * place to the top of element!
23064 this.picker().addClass('top');
23065 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23070 this.picker().addClass('bottom');
23072 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23075 onFocus : function()
23077 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23081 onBlur : function()
23083 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23085 var d = this.inputEl().getValue();
23094 this.picker().show();
23095 this.picker().select('>.datepicker-months', true).first().show();
23099 this.fireEvent('show', this, this.date);
23104 if(this.isInline) {
23107 this.picker().hide();
23108 this.fireEvent('hide', this, this.date);
23112 onMousedown: function(e)
23114 e.stopPropagation();
23115 e.preventDefault();
23120 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23124 fireKey: function(e)
23126 if (!this.picker().isVisible()){
23127 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23138 e.preventDefault();
23142 dir = e.keyCode == 37 ? -1 : 1;
23144 this.vIndex = this.vIndex + dir;
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]);
23164 dir = e.keyCode == 38 ? -1 : 1;
23166 this.vIndex = this.vIndex + dir * 4;
23168 if(this.vIndex < 0){
23172 if(this.vIndex > 11){
23176 if(isNaN(this.vIndex)){
23180 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23185 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23186 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23190 e.preventDefault();
23193 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23194 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23210 this.picker().remove();
23215 Roo.apply(Roo.bootstrap.MonthField, {
23234 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23235 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23240 Roo.apply(Roo.bootstrap.MonthField, {
23244 cls: 'datepicker dropdown-menu roo-dynamic',
23248 cls: 'datepicker-months',
23252 cls: 'table-condensed',
23254 Roo.bootstrap.DateField.content
23274 * @class Roo.bootstrap.CheckBox
23275 * @extends Roo.bootstrap.Input
23276 * Bootstrap CheckBox class
23278 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23279 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23280 * @cfg {String} boxLabel The text that appears beside the checkbox
23281 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23282 * @cfg {Boolean} checked initnal the element
23283 * @cfg {Boolean} inline inline the element (default false)
23284 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23285 * @cfg {String} tooltip label tooltip
23288 * Create a new CheckBox
23289 * @param {Object} config The config object
23292 Roo.bootstrap.CheckBox = function(config){
23293 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23298 * Fires when the element is checked or unchecked.
23299 * @param {Roo.bootstrap.CheckBox} this This input
23300 * @param {Boolean} checked The new checked value
23305 * Fires when the element is click.
23306 * @param {Roo.bootstrap.CheckBox} this This input
23313 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23315 inputType: 'checkbox',
23324 // checkbox success does not make any sense really..
23329 getAutoCreate : function()
23331 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23337 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23340 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23346 type : this.inputType,
23347 value : this.inputValue,
23348 cls : 'roo-' + this.inputType, //'form-box',
23349 placeholder : this.placeholder || ''
23353 if(this.inputType != 'radio'){
23357 cls : 'roo-hidden-value',
23358 value : this.checked ? this.inputValue : this.valueOff
23363 if (this.weight) { // Validity check?
23364 cfg.cls += " " + this.inputType + "-" + this.weight;
23367 if (this.disabled) {
23368 input.disabled=true;
23372 input.checked = this.checked;
23377 input.name = this.name;
23379 if(this.inputType != 'radio'){
23380 hidden.name = this.name;
23381 input.name = '_hidden_' + this.name;
23386 input.cls += ' input-' + this.size;
23391 ['xs','sm','md','lg'].map(function(size){
23392 if (settings[size]) {
23393 cfg.cls += ' col-' + size + '-' + settings[size];
23397 var inputblock = input;
23399 if (this.before || this.after) {
23402 cls : 'input-group',
23407 inputblock.cn.push({
23409 cls : 'input-group-addon',
23414 inputblock.cn.push(input);
23416 if(this.inputType != 'radio'){
23417 inputblock.cn.push(hidden);
23421 inputblock.cn.push({
23423 cls : 'input-group-addon',
23429 var boxLabelCfg = false;
23435 //'for': id, // box label is handled by onclick - so no for...
23437 html: this.boxLabel
23440 boxLabelCfg.tooltip = this.tooltip;
23446 if (align ==='left' && this.fieldLabel.length) {
23447 // Roo.log("left and has label");
23452 cls : 'control-label',
23453 html : this.fieldLabel
23464 cfg.cn[1].cn.push(boxLabelCfg);
23467 if(this.labelWidth > 12){
23468 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23471 if(this.labelWidth < 13 && this.labelmd == 0){
23472 this.labelmd = this.labelWidth;
23475 if(this.labellg > 0){
23476 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23477 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23480 if(this.labelmd > 0){
23481 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23482 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23485 if(this.labelsm > 0){
23486 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23487 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23490 if(this.labelxs > 0){
23491 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23492 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23495 } else if ( this.fieldLabel.length) {
23496 // Roo.log(" label");
23500 tag: this.boxLabel ? 'span' : 'label',
23502 cls: 'control-label box-input-label',
23503 //cls : 'input-group-addon',
23504 html : this.fieldLabel
23511 cfg.cn.push(boxLabelCfg);
23516 // Roo.log(" no label && no align");
23517 cfg.cn = [ inputblock ] ;
23519 cfg.cn.push(boxLabelCfg);
23527 if(this.inputType != 'radio'){
23528 cfg.cn.push(hidden);
23536 * return the real input element.
23538 inputEl: function ()
23540 return this.el.select('input.roo-' + this.inputType,true).first();
23542 hiddenEl: function ()
23544 return this.el.select('input.roo-hidden-value',true).first();
23547 labelEl: function()
23549 return this.el.select('label.control-label',true).first();
23551 /* depricated... */
23555 return this.labelEl();
23558 boxLabelEl: function()
23560 return this.el.select('label.box-label',true).first();
23563 initEvents : function()
23565 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23567 this.inputEl().on('click', this.onClick, this);
23569 if (this.boxLabel) {
23570 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23573 this.startValue = this.getValue();
23576 Roo.bootstrap.CheckBox.register(this);
23580 onClick : function(e)
23582 if(this.fireEvent('click', this, e) !== false){
23583 this.setChecked(!this.checked);
23588 setChecked : function(state,suppressEvent)
23590 this.startValue = this.getValue();
23592 if(this.inputType == 'radio'){
23594 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23595 e.dom.checked = false;
23598 this.inputEl().dom.checked = true;
23600 this.inputEl().dom.value = this.inputValue;
23602 if(suppressEvent !== true){
23603 this.fireEvent('check', this, true);
23611 this.checked = state;
23613 this.inputEl().dom.checked = state;
23616 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23618 if(suppressEvent !== true){
23619 this.fireEvent('check', this, state);
23625 getValue : function()
23627 if(this.inputType == 'radio'){
23628 return this.getGroupValue();
23631 return this.hiddenEl().dom.value;
23635 getGroupValue : function()
23637 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23641 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23644 setValue : function(v,suppressEvent)
23646 if(this.inputType == 'radio'){
23647 this.setGroupValue(v, suppressEvent);
23651 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23656 setGroupValue : function(v, suppressEvent)
23658 this.startValue = this.getValue();
23660 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23661 e.dom.checked = false;
23663 if(e.dom.value == v){
23664 e.dom.checked = true;
23668 if(suppressEvent !== true){
23669 this.fireEvent('check', this, true);
23677 validate : function()
23679 if(this.getVisibilityEl().hasClass('hidden')){
23685 (this.inputType == 'radio' && this.validateRadio()) ||
23686 (this.inputType == 'checkbox' && this.validateCheckbox())
23692 this.markInvalid();
23696 validateRadio : function()
23698 if(this.getVisibilityEl().hasClass('hidden')){
23702 if(this.allowBlank){
23708 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23709 if(!e.dom.checked){
23721 validateCheckbox : function()
23724 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23725 //return (this.getValue() == this.inputValue) ? true : false;
23728 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23736 for(var i in group){
23737 if(group[i].el.isVisible(true)){
23745 for(var i in group){
23750 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23757 * Mark this field as valid
23759 markValid : function()
23763 this.fireEvent('valid', this);
23765 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23768 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23775 if(this.inputType == 'radio'){
23776 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23777 var fg = e.findParent('.form-group', false, true);
23778 if (Roo.bootstrap.version == 3) {
23779 fg.removeClass([_this.invalidClass, _this.validClass]);
23780 fg.addClass(_this.validClass);
23782 fg.removeClass(['is-valid', 'is-invalid']);
23783 fg.addClass('is-valid');
23791 var fg = this.el.findParent('.form-group', false, true);
23792 if (Roo.bootstrap.version == 3) {
23793 fg.removeClass([this.invalidClass, this.validClass]);
23794 fg.addClass(this.validClass);
23796 fg.removeClass(['is-valid', 'is-invalid']);
23797 fg.addClass('is-valid');
23802 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23808 for(var i in group){
23809 var fg = group[i].el.findParent('.form-group', false, true);
23810 if (Roo.bootstrap.version == 3) {
23811 fg.removeClass([this.invalidClass, this.validClass]);
23812 fg.addClass(this.validClass);
23814 fg.removeClass(['is-valid', 'is-invalid']);
23815 fg.addClass('is-valid');
23821 * Mark this field as invalid
23822 * @param {String} msg The validation message
23824 markInvalid : function(msg)
23826 if(this.allowBlank){
23832 this.fireEvent('invalid', this, msg);
23834 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23837 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23841 label.markInvalid();
23844 if(this.inputType == 'radio'){
23846 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23847 var fg = e.findParent('.form-group', false, true);
23848 if (Roo.bootstrap.version == 3) {
23849 fg.removeClass([_this.invalidClass, _this.validClass]);
23850 fg.addClass(_this.invalidClass);
23852 fg.removeClass(['is-invalid', 'is-valid']);
23853 fg.addClass('is-invalid');
23861 var fg = this.el.findParent('.form-group', false, true);
23862 if (Roo.bootstrap.version == 3) {
23863 fg.removeClass([_this.invalidClass, _this.validClass]);
23864 fg.addClass(_this.invalidClass);
23866 fg.removeClass(['is-invalid', 'is-valid']);
23867 fg.addClass('is-invalid');
23872 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23878 for(var i in group){
23879 var fg = group[i].el.findParent('.form-group', false, true);
23880 if (Roo.bootstrap.version == 3) {
23881 fg.removeClass([_this.invalidClass, _this.validClass]);
23882 fg.addClass(_this.invalidClass);
23884 fg.removeClass(['is-invalid', 'is-valid']);
23885 fg.addClass('is-invalid');
23891 clearInvalid : function()
23893 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23895 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23897 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23899 if (label && label.iconEl) {
23900 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23901 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23905 disable : function()
23907 if(this.inputType != 'radio'){
23908 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23915 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23916 _this.getActionEl().addClass(this.disabledClass);
23917 e.dom.disabled = true;
23921 this.disabled = true;
23922 this.fireEvent("disable", this);
23926 enable : function()
23928 if(this.inputType != 'radio'){
23929 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23936 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23937 _this.getActionEl().removeClass(this.disabledClass);
23938 e.dom.disabled = false;
23942 this.disabled = false;
23943 this.fireEvent("enable", this);
23947 setBoxLabel : function(v)
23952 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23958 Roo.apply(Roo.bootstrap.CheckBox, {
23963 * register a CheckBox Group
23964 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23966 register : function(checkbox)
23968 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23969 this.groups[checkbox.groupId] = {};
23972 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23976 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23980 * fetch a CheckBox Group based on the group ID
23981 * @param {string} the group ID
23982 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23984 get: function(groupId) {
23985 if (typeof(this.groups[groupId]) == 'undefined') {
23989 return this.groups[groupId] ;
24002 * @class Roo.bootstrap.Radio
24003 * @extends Roo.bootstrap.Component
24004 * Bootstrap Radio class
24005 * @cfg {String} boxLabel - the label associated
24006 * @cfg {String} value - the value of radio
24009 * Create a new Radio
24010 * @param {Object} config The config object
24012 Roo.bootstrap.Radio = function(config){
24013 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24017 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24023 getAutoCreate : function()
24027 cls : 'form-group radio',
24032 html : this.boxLabel
24040 initEvents : function()
24042 this.parent().register(this);
24044 this.el.on('click', this.onClick, this);
24048 onClick : function(e)
24050 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24051 this.setChecked(true);
24055 setChecked : function(state, suppressEvent)
24057 this.parent().setValue(this.value, suppressEvent);
24061 setBoxLabel : function(v)
24066 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24081 * @class Roo.bootstrap.SecurePass
24082 * @extends Roo.bootstrap.Input
24083 * Bootstrap SecurePass class
24087 * Create a new SecurePass
24088 * @param {Object} config The config object
24091 Roo.bootstrap.SecurePass = function (config) {
24092 // these go here, so the translation tool can replace them..
24094 PwdEmpty: "Please type a password, and then retype it to confirm.",
24095 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24096 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24097 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24098 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24099 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24100 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24101 TooWeak: "Your password is Too Weak."
24103 this.meterLabel = "Password strength:";
24104 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24105 this.meterClass = [
24106 "roo-password-meter-tooweak",
24107 "roo-password-meter-weak",
24108 "roo-password-meter-medium",
24109 "roo-password-meter-strong",
24110 "roo-password-meter-grey"
24115 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24118 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24120 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24122 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24123 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24124 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24125 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24126 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24127 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24128 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24138 * @cfg {String/Object} Label for the strength meter (defaults to
24139 * 'Password strength:')
24144 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24145 * ['Weak', 'Medium', 'Strong'])
24148 pwdStrengths: false,
24161 initEvents: function ()
24163 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24165 if (this.el.is('input[type=password]') && Roo.isSafari) {
24166 this.el.on('keydown', this.SafariOnKeyDown, this);
24169 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24172 onRender: function (ct, position)
24174 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24175 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24176 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24178 this.trigger.createChild({
24183 cls: 'roo-password-meter-grey col-xs-12',
24186 //width: this.meterWidth + 'px'
24190 cls: 'roo-password-meter-text'
24196 if (this.hideTrigger) {
24197 this.trigger.setDisplayed(false);
24199 this.setSize(this.width || '', this.height || '');
24202 onDestroy: function ()
24204 if (this.trigger) {
24205 this.trigger.removeAllListeners();
24206 this.trigger.remove();
24209 this.wrap.remove();
24211 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24214 checkStrength: function ()
24216 var pwd = this.inputEl().getValue();
24217 if (pwd == this._lastPwd) {
24222 if (this.ClientSideStrongPassword(pwd)) {
24224 } else if (this.ClientSideMediumPassword(pwd)) {
24226 } else if (this.ClientSideWeakPassword(pwd)) {
24232 Roo.log('strength1: ' + strength);
24234 //var pm = this.trigger.child('div/div/div').dom;
24235 var pm = this.trigger.child('div/div');
24236 pm.removeClass(this.meterClass);
24237 pm.addClass(this.meterClass[strength]);
24240 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24242 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24244 this._lastPwd = pwd;
24248 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24250 this._lastPwd = '';
24252 var pm = this.trigger.child('div/div');
24253 pm.removeClass(this.meterClass);
24254 pm.addClass('roo-password-meter-grey');
24257 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24260 this.inputEl().dom.type='password';
24263 validateValue: function (value)
24265 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24268 if (value.length == 0) {
24269 if (this.allowBlank) {
24270 this.clearInvalid();
24274 this.markInvalid(this.errors.PwdEmpty);
24275 this.errorMsg = this.errors.PwdEmpty;
24283 if (!value.match(/[\x21-\x7e]+/)) {
24284 this.markInvalid(this.errors.PwdBadChar);
24285 this.errorMsg = this.errors.PwdBadChar;
24288 if (value.length < 6) {
24289 this.markInvalid(this.errors.PwdShort);
24290 this.errorMsg = this.errors.PwdShort;
24293 if (value.length > 16) {
24294 this.markInvalid(this.errors.PwdLong);
24295 this.errorMsg = this.errors.PwdLong;
24299 if (this.ClientSideStrongPassword(value)) {
24301 } else if (this.ClientSideMediumPassword(value)) {
24303 } else if (this.ClientSideWeakPassword(value)) {
24310 if (strength < 2) {
24311 //this.markInvalid(this.errors.TooWeak);
24312 this.errorMsg = this.errors.TooWeak;
24317 console.log('strength2: ' + strength);
24319 //var pm = this.trigger.child('div/div/div').dom;
24321 var pm = this.trigger.child('div/div');
24322 pm.removeClass(this.meterClass);
24323 pm.addClass(this.meterClass[strength]);
24325 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24327 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24329 this.errorMsg = '';
24333 CharacterSetChecks: function (type)
24336 this.fResult = false;
24339 isctype: function (character, type)
24342 case this.kCapitalLetter:
24343 if (character >= 'A' && character <= 'Z') {
24348 case this.kSmallLetter:
24349 if (character >= 'a' && character <= 'z') {
24355 if (character >= '0' && character <= '9') {
24360 case this.kPunctuation:
24361 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24372 IsLongEnough: function (pwd, size)
24374 return !(pwd == null || isNaN(size) || pwd.length < size);
24377 SpansEnoughCharacterSets: function (word, nb)
24379 if (!this.IsLongEnough(word, nb))
24384 var characterSetChecks = new Array(
24385 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24386 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24389 for (var index = 0; index < word.length; ++index) {
24390 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24391 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24392 characterSetChecks[nCharSet].fResult = true;
24399 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24400 if (characterSetChecks[nCharSet].fResult) {
24405 if (nCharSets < nb) {
24411 ClientSideStrongPassword: function (pwd)
24413 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24416 ClientSideMediumPassword: function (pwd)
24418 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24421 ClientSideWeakPassword: function (pwd)
24423 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24426 })//<script type="text/javascript">
24429 * Based Ext JS Library 1.1.1
24430 * Copyright(c) 2006-2007, Ext JS, LLC.
24436 * @class Roo.HtmlEditorCore
24437 * @extends Roo.Component
24438 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24440 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24443 Roo.HtmlEditorCore = function(config){
24446 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24451 * @event initialize
24452 * Fires when the editor is fully initialized (including the iframe)
24453 * @param {Roo.HtmlEditorCore} this
24458 * Fires when the editor is first receives the focus. Any insertion must wait
24459 * until after this event.
24460 * @param {Roo.HtmlEditorCore} this
24464 * @event beforesync
24465 * Fires before the textarea is updated with content from the editor iframe. Return false
24466 * to cancel the sync.
24467 * @param {Roo.HtmlEditorCore} this
24468 * @param {String} html
24472 * @event beforepush
24473 * Fires before the iframe editor is updated with content from the textarea. Return false
24474 * to cancel the push.
24475 * @param {Roo.HtmlEditorCore} this
24476 * @param {String} html
24481 * Fires when the textarea is updated with content from the editor iframe.
24482 * @param {Roo.HtmlEditorCore} this
24483 * @param {String} html
24488 * Fires when the iframe editor is updated with content from the textarea.
24489 * @param {Roo.HtmlEditorCore} this
24490 * @param {String} html
24495 * @event editorevent
24496 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24497 * @param {Roo.HtmlEditorCore} this
24503 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24505 // defaults : white / black...
24506 this.applyBlacklists();
24513 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24517 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24523 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24528 * @cfg {Number} height (in pixels)
24532 * @cfg {Number} width (in pixels)
24537 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24540 stylesheets: false,
24545 // private properties
24546 validationEvent : false,
24548 initialized : false,
24550 sourceEditMode : false,
24551 onFocus : Roo.emptyFn,
24553 hideMode:'offsets',
24557 // blacklist + whitelisted elements..
24564 * Protected method that will not generally be called directly. It
24565 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24566 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24568 getDocMarkup : function(){
24572 // inherit styels from page...??
24573 if (this.stylesheets === false) {
24575 Roo.get(document.head).select('style').each(function(node) {
24576 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24579 Roo.get(document.head).select('link').each(function(node) {
24580 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24583 } else if (!this.stylesheets.length) {
24585 st = '<style type="text/css">' +
24586 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24589 for (var i in this.stylesheets) {
24590 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24595 st += '<style type="text/css">' +
24596 'IMG { cursor: pointer } ' +
24599 var cls = 'roo-htmleditor-body';
24601 if(this.bodyCls.length){
24602 cls += ' ' + this.bodyCls;
24605 return '<html><head>' + st +
24606 //<style type="text/css">' +
24607 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24609 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24613 onRender : function(ct, position)
24616 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24617 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24620 this.el.dom.style.border = '0 none';
24621 this.el.dom.setAttribute('tabIndex', -1);
24622 this.el.addClass('x-hidden hide');
24626 if(Roo.isIE){ // fix IE 1px bogus margin
24627 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24631 this.frameId = Roo.id();
24635 var iframe = this.owner.wrap.createChild({
24637 cls: 'form-control', // bootstrap..
24639 name: this.frameId,
24640 frameBorder : 'no',
24641 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24646 this.iframe = iframe.dom;
24648 this.assignDocWin();
24650 this.doc.designMode = 'on';
24653 this.doc.write(this.getDocMarkup());
24657 var task = { // must defer to wait for browser to be ready
24659 //console.log("run task?" + this.doc.readyState);
24660 this.assignDocWin();
24661 if(this.doc.body || this.doc.readyState == 'complete'){
24663 this.doc.designMode="on";
24667 Roo.TaskMgr.stop(task);
24668 this.initEditor.defer(10, this);
24675 Roo.TaskMgr.start(task);
24680 onResize : function(w, h)
24682 Roo.log('resize: ' +w + ',' + h );
24683 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24687 if(typeof w == 'number'){
24689 this.iframe.style.width = w + 'px';
24691 if(typeof h == 'number'){
24693 this.iframe.style.height = h + 'px';
24695 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24702 * Toggles the editor between standard and source edit mode.
24703 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24705 toggleSourceEdit : function(sourceEditMode){
24707 this.sourceEditMode = sourceEditMode === true;
24709 if(this.sourceEditMode){
24711 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24714 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24715 //this.iframe.className = '';
24718 //this.setSize(this.owner.wrap.getSize());
24719 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24726 * Protected method that will not generally be called directly. If you need/want
24727 * custom HTML cleanup, this is the method you should override.
24728 * @param {String} html The HTML to be cleaned
24729 * return {String} The cleaned HTML
24731 cleanHtml : function(html){
24732 html = String(html);
24733 if(html.length > 5){
24734 if(Roo.isSafari){ // strip safari nonsense
24735 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24738 if(html == ' '){
24745 * HTML Editor -> Textarea
24746 * Protected method that will not generally be called directly. Syncs the contents
24747 * of the editor iframe with the textarea.
24749 syncValue : function(){
24750 if(this.initialized){
24751 var bd = (this.doc.body || this.doc.documentElement);
24752 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24753 var html = bd.innerHTML;
24755 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24756 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24758 html = '<div style="'+m[0]+'">' + html + '</div>';
24761 html = this.cleanHtml(html);
24762 // fix up the special chars.. normaly like back quotes in word...
24763 // however we do not want to do this with chinese..
24764 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24766 var cc = match.charCodeAt();
24768 // Get the character value, handling surrogate pairs
24769 if (match.length == 2) {
24770 // It's a surrogate pair, calculate the Unicode code point
24771 var high = match.charCodeAt(0) - 0xD800;
24772 var low = match.charCodeAt(1) - 0xDC00;
24773 cc = (high * 0x400) + low + 0x10000;
24775 (cc >= 0x4E00 && cc < 0xA000 ) ||
24776 (cc >= 0x3400 && cc < 0x4E00 ) ||
24777 (cc >= 0xf900 && cc < 0xfb00 )
24782 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24783 return "&#" + cc + ";";
24790 if(this.owner.fireEvent('beforesync', this, html) !== false){
24791 this.el.dom.value = html;
24792 this.owner.fireEvent('sync', this, html);
24798 * Protected method that will not generally be called directly. Pushes the value of the textarea
24799 * into the iframe editor.
24801 pushValue : function(){
24802 if(this.initialized){
24803 var v = this.el.dom.value.trim();
24805 // if(v.length < 1){
24809 if(this.owner.fireEvent('beforepush', this, v) !== false){
24810 var d = (this.doc.body || this.doc.documentElement);
24812 this.cleanUpPaste();
24813 this.el.dom.value = d.innerHTML;
24814 this.owner.fireEvent('push', this, v);
24820 deferFocus : function(){
24821 this.focus.defer(10, this);
24825 focus : function(){
24826 if(this.win && !this.sourceEditMode){
24833 assignDocWin: function()
24835 var iframe = this.iframe;
24838 this.doc = iframe.contentWindow.document;
24839 this.win = iframe.contentWindow;
24841 // if (!Roo.get(this.frameId)) {
24844 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24845 // this.win = Roo.get(this.frameId).dom.contentWindow;
24847 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24851 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24852 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24857 initEditor : function(){
24858 //console.log("INIT EDITOR");
24859 this.assignDocWin();
24863 this.doc.designMode="on";
24865 this.doc.write(this.getDocMarkup());
24868 var dbody = (this.doc.body || this.doc.documentElement);
24869 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24870 // this copies styles from the containing element into thsi one..
24871 // not sure why we need all of this..
24872 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24874 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24875 //ss['background-attachment'] = 'fixed'; // w3c
24876 dbody.bgProperties = 'fixed'; // ie
24877 //Roo.DomHelper.applyStyles(dbody, ss);
24878 Roo.EventManager.on(this.doc, {
24879 //'mousedown': this.onEditorEvent,
24880 'mouseup': this.onEditorEvent,
24881 'dblclick': this.onEditorEvent,
24882 'click': this.onEditorEvent,
24883 'keyup': this.onEditorEvent,
24888 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24890 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24891 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24893 this.initialized = true;
24895 this.owner.fireEvent('initialize', this);
24900 onDestroy : function(){
24906 //for (var i =0; i < this.toolbars.length;i++) {
24907 // // fixme - ask toolbars for heights?
24908 // this.toolbars[i].onDestroy();
24911 //this.wrap.dom.innerHTML = '';
24912 //this.wrap.remove();
24917 onFirstFocus : function(){
24919 this.assignDocWin();
24922 this.activated = true;
24925 if(Roo.isGecko){ // prevent silly gecko errors
24927 var s = this.win.getSelection();
24928 if(!s.focusNode || s.focusNode.nodeType != 3){
24929 var r = s.getRangeAt(0);
24930 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24935 this.execCmd('useCSS', true);
24936 this.execCmd('styleWithCSS', false);
24939 this.owner.fireEvent('activate', this);
24943 adjustFont: function(btn){
24944 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24945 //if(Roo.isSafari){ // safari
24948 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24949 if(Roo.isSafari){ // safari
24950 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24951 v = (v < 10) ? 10 : v;
24952 v = (v > 48) ? 48 : v;
24953 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24958 v = Math.max(1, v+adjust);
24960 this.execCmd('FontSize', v );
24963 onEditorEvent : function(e)
24965 this.owner.fireEvent('editorevent', this, e);
24966 // this.updateToolbar();
24967 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24970 insertTag : function(tg)
24972 // could be a bit smarter... -> wrap the current selected tRoo..
24973 if (tg.toLowerCase() == 'span' ||
24974 tg.toLowerCase() == 'code' ||
24975 tg.toLowerCase() == 'sup' ||
24976 tg.toLowerCase() == 'sub'
24979 range = this.createRange(this.getSelection());
24980 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24981 wrappingNode.appendChild(range.extractContents());
24982 range.insertNode(wrappingNode);
24989 this.execCmd("formatblock", tg);
24993 insertText : function(txt)
24997 var range = this.createRange();
24998 range.deleteContents();
24999 //alert(Sender.getAttribute('label'));
25001 range.insertNode(this.doc.createTextNode(txt));
25007 * Executes a Midas editor command on the editor document and performs necessary focus and
25008 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25009 * @param {String} cmd The Midas command
25010 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25012 relayCmd : function(cmd, value){
25014 this.execCmd(cmd, value);
25015 this.owner.fireEvent('editorevent', this);
25016 //this.updateToolbar();
25017 this.owner.deferFocus();
25021 * Executes a Midas editor command directly on the editor document.
25022 * For visual commands, you should use {@link #relayCmd} instead.
25023 * <b>This should only be called after the editor is initialized.</b>
25024 * @param {String} cmd The Midas command
25025 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25027 execCmd : function(cmd, value){
25028 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25035 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25037 * @param {String} text | dom node..
25039 insertAtCursor : function(text)
25042 if(!this.activated){
25048 var r = this.doc.selection.createRange();
25059 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25063 // from jquery ui (MIT licenced)
25065 var win = this.win;
25067 if (win.getSelection && win.getSelection().getRangeAt) {
25068 range = win.getSelection().getRangeAt(0);
25069 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25070 range.insertNode(node);
25071 } else if (win.document.selection && win.document.selection.createRange) {
25072 // no firefox support
25073 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25074 win.document.selection.createRange().pasteHTML(txt);
25076 // no firefox support
25077 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25078 this.execCmd('InsertHTML', txt);
25087 mozKeyPress : function(e){
25089 var c = e.getCharCode(), cmd;
25092 c = String.fromCharCode(c).toLowerCase();
25106 this.cleanUpPaste.defer(100, this);
25114 e.preventDefault();
25122 fixKeys : function(){ // load time branching for fastest keydown performance
25124 return function(e){
25125 var k = e.getKey(), r;
25128 r = this.doc.selection.createRange();
25131 r.pasteHTML('    ');
25138 r = this.doc.selection.createRange();
25140 var target = r.parentElement();
25141 if(!target || target.tagName.toLowerCase() != 'li'){
25143 r.pasteHTML('<br />');
25149 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25150 this.cleanUpPaste.defer(100, this);
25156 }else if(Roo.isOpera){
25157 return function(e){
25158 var k = e.getKey();
25162 this.execCmd('InsertHTML','    ');
25165 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25166 this.cleanUpPaste.defer(100, this);
25171 }else if(Roo.isSafari){
25172 return function(e){
25173 var k = e.getKey();
25177 this.execCmd('InsertText','\t');
25181 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25182 this.cleanUpPaste.defer(100, this);
25190 getAllAncestors: function()
25192 var p = this.getSelectedNode();
25195 a.push(p); // push blank onto stack..
25196 p = this.getParentElement();
25200 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25204 a.push(this.doc.body);
25208 lastSelNode : false,
25211 getSelection : function()
25213 this.assignDocWin();
25214 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25217 getSelectedNode: function()
25219 // this may only work on Gecko!!!
25221 // should we cache this!!!!
25226 var range = this.createRange(this.getSelection()).cloneRange();
25229 var parent = range.parentElement();
25231 var testRange = range.duplicate();
25232 testRange.moveToElementText(parent);
25233 if (testRange.inRange(range)) {
25236 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25239 parent = parent.parentElement;
25244 // is ancestor a text element.
25245 var ac = range.commonAncestorContainer;
25246 if (ac.nodeType == 3) {
25247 ac = ac.parentNode;
25250 var ar = ac.childNodes;
25253 var other_nodes = [];
25254 var has_other_nodes = false;
25255 for (var i=0;i<ar.length;i++) {
25256 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25259 // fullly contained node.
25261 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25266 // probably selected..
25267 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25268 other_nodes.push(ar[i]);
25272 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25277 has_other_nodes = true;
25279 if (!nodes.length && other_nodes.length) {
25280 nodes= other_nodes;
25282 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25288 createRange: function(sel)
25290 // this has strange effects when using with
25291 // top toolbar - not sure if it's a great idea.
25292 //this.editor.contentWindow.focus();
25293 if (typeof sel != "undefined") {
25295 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25297 return this.doc.createRange();
25300 return this.doc.createRange();
25303 getParentElement: function()
25306 this.assignDocWin();
25307 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25309 var range = this.createRange(sel);
25312 var p = range.commonAncestorContainer;
25313 while (p.nodeType == 3) { // text node
25324 * Range intersection.. the hard stuff...
25328 * [ -- selected range --- ]
25332 * if end is before start or hits it. fail.
25333 * if start is after end or hits it fail.
25335 * if either hits (but other is outside. - then it's not
25341 // @see http://www.thismuchiknow.co.uk/?p=64.
25342 rangeIntersectsNode : function(range, node)
25344 var nodeRange = node.ownerDocument.createRange();
25346 nodeRange.selectNode(node);
25348 nodeRange.selectNodeContents(node);
25351 var rangeStartRange = range.cloneRange();
25352 rangeStartRange.collapse(true);
25354 var rangeEndRange = range.cloneRange();
25355 rangeEndRange.collapse(false);
25357 var nodeStartRange = nodeRange.cloneRange();
25358 nodeStartRange.collapse(true);
25360 var nodeEndRange = nodeRange.cloneRange();
25361 nodeEndRange.collapse(false);
25363 return rangeStartRange.compareBoundaryPoints(
25364 Range.START_TO_START, nodeEndRange) == -1 &&
25365 rangeEndRange.compareBoundaryPoints(
25366 Range.START_TO_START, nodeStartRange) == 1;
25370 rangeCompareNode : function(range, node)
25372 var nodeRange = node.ownerDocument.createRange();
25374 nodeRange.selectNode(node);
25376 nodeRange.selectNodeContents(node);
25380 range.collapse(true);
25382 nodeRange.collapse(true);
25384 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25385 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25387 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25389 var nodeIsBefore = ss == 1;
25390 var nodeIsAfter = ee == -1;
25392 if (nodeIsBefore && nodeIsAfter) {
25395 if (!nodeIsBefore && nodeIsAfter) {
25396 return 1; //right trailed.
25399 if (nodeIsBefore && !nodeIsAfter) {
25400 return 2; // left trailed.
25406 // private? - in a new class?
25407 cleanUpPaste : function()
25409 // cleans up the whole document..
25410 Roo.log('cleanuppaste');
25412 this.cleanUpChildren(this.doc.body);
25413 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25414 if (clean != this.doc.body.innerHTML) {
25415 this.doc.body.innerHTML = clean;
25420 cleanWordChars : function(input) {// change the chars to hex code
25421 var he = Roo.HtmlEditorCore;
25423 var output = input;
25424 Roo.each(he.swapCodes, function(sw) {
25425 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25427 output = output.replace(swapper, sw[1]);
25434 cleanUpChildren : function (n)
25436 if (!n.childNodes.length) {
25439 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25440 this.cleanUpChild(n.childNodes[i]);
25447 cleanUpChild : function (node)
25450 //console.log(node);
25451 if (node.nodeName == "#text") {
25452 // clean up silly Windows -- stuff?
25455 if (node.nodeName == "#comment") {
25456 node.parentNode.removeChild(node);
25457 // clean up silly Windows -- stuff?
25460 var lcname = node.tagName.toLowerCase();
25461 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25462 // whitelist of tags..
25464 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25466 node.parentNode.removeChild(node);
25471 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25473 // spans with no attributes - just remove them..
25474 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25475 remove_keep_children = true;
25478 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25479 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25481 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25482 // remove_keep_children = true;
25485 if (remove_keep_children) {
25486 this.cleanUpChildren(node);
25487 // inserts everything just before this node...
25488 while (node.childNodes.length) {
25489 var cn = node.childNodes[0];
25490 node.removeChild(cn);
25491 node.parentNode.insertBefore(cn, node);
25493 node.parentNode.removeChild(node);
25497 if (!node.attributes || !node.attributes.length) {
25502 this.cleanUpChildren(node);
25506 function cleanAttr(n,v)
25509 if (v.match(/^\./) || v.match(/^\//)) {
25512 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25515 if (v.match(/^#/)) {
25518 if (v.match(/^\{/)) { // allow template editing.
25521 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25522 node.removeAttribute(n);
25526 var cwhite = this.cwhite;
25527 var cblack = this.cblack;
25529 function cleanStyle(n,v)
25531 if (v.match(/expression/)) { //XSS?? should we even bother..
25532 node.removeAttribute(n);
25536 var parts = v.split(/;/);
25539 Roo.each(parts, function(p) {
25540 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25544 var l = p.split(':').shift().replace(/\s+/g,'');
25545 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25547 if ( cwhite.length && cblack.indexOf(l) > -1) {
25548 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25549 //node.removeAttribute(n);
25553 // only allow 'c whitelisted system attributes'
25554 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25555 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25556 //node.removeAttribute(n);
25566 if (clean.length) {
25567 node.setAttribute(n, clean.join(';'));
25569 node.removeAttribute(n);
25575 for (var i = node.attributes.length-1; i > -1 ; i--) {
25576 var a = node.attributes[i];
25579 if (a.name.toLowerCase().substr(0,2)=='on') {
25580 node.removeAttribute(a.name);
25583 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25584 node.removeAttribute(a.name);
25587 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25588 cleanAttr(a.name,a.value); // fixme..
25591 if (a.name == 'style') {
25592 cleanStyle(a.name,a.value);
25595 /// clean up MS crap..
25596 // tecnically this should be a list of valid class'es..
25599 if (a.name == 'class') {
25600 if (a.value.match(/^Mso/)) {
25601 node.removeAttribute('class');
25604 if (a.value.match(/^body$/)) {
25605 node.removeAttribute('class');
25616 this.cleanUpChildren(node);
25622 * Clean up MS wordisms...
25624 cleanWord : function(node)
25627 this.cleanWord(this.doc.body);
25632 node.nodeName == 'SPAN' &&
25633 !node.hasAttributes() &&
25634 node.childNodes.length == 1 &&
25635 node.firstChild.nodeName == "#text"
25637 var textNode = node.firstChild;
25638 node.removeChild(textNode);
25639 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25640 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25642 node.parentNode.insertBefore(textNode, node);
25643 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25644 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25646 node.parentNode.removeChild(node);
25649 if (node.nodeName == "#text") {
25650 // clean up silly Windows -- stuff?
25653 if (node.nodeName == "#comment") {
25654 node.parentNode.removeChild(node);
25655 // clean up silly Windows -- stuff?
25659 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25660 node.parentNode.removeChild(node);
25663 //Roo.log(node.tagName);
25664 // remove - but keep children..
25665 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25666 //Roo.log('-- removed');
25667 while (node.childNodes.length) {
25668 var cn = node.childNodes[0];
25669 node.removeChild(cn);
25670 node.parentNode.insertBefore(cn, node);
25671 // move node to parent - and clean it..
25672 this.cleanWord(cn);
25674 node.parentNode.removeChild(node);
25675 /// no need to iterate chidlren = it's got none..
25676 //this.iterateChildren(node, this.cleanWord);
25680 if (node.className.length) {
25682 var cn = node.className.split(/\W+/);
25684 Roo.each(cn, function(cls) {
25685 if (cls.match(/Mso[a-zA-Z]+/)) {
25690 node.className = cna.length ? cna.join(' ') : '';
25692 node.removeAttribute("class");
25696 if (node.hasAttribute("lang")) {
25697 node.removeAttribute("lang");
25700 if (node.hasAttribute("style")) {
25702 var styles = node.getAttribute("style").split(";");
25704 Roo.each(styles, function(s) {
25705 if (!s.match(/:/)) {
25708 var kv = s.split(":");
25709 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25712 // what ever is left... we allow.
25715 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25716 if (!nstyle.length) {
25717 node.removeAttribute('style');
25720 this.iterateChildren(node, this.cleanWord);
25726 * iterateChildren of a Node, calling fn each time, using this as the scole..
25727 * @param {DomNode} node node to iterate children of.
25728 * @param {Function} fn method of this class to call on each item.
25730 iterateChildren : function(node, fn)
25732 if (!node.childNodes.length) {
25735 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25736 fn.call(this, node.childNodes[i])
25742 * cleanTableWidths.
25744 * Quite often pasting from word etc.. results in tables with column and widths.
25745 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25748 cleanTableWidths : function(node)
25753 this.cleanTableWidths(this.doc.body);
25758 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25761 Roo.log(node.tagName);
25762 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25763 this.iterateChildren(node, this.cleanTableWidths);
25766 if (node.hasAttribute('width')) {
25767 node.removeAttribute('width');
25771 if (node.hasAttribute("style")) {
25774 var styles = node.getAttribute("style").split(";");
25776 Roo.each(styles, function(s) {
25777 if (!s.match(/:/)) {
25780 var kv = s.split(":");
25781 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25784 // what ever is left... we allow.
25787 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25788 if (!nstyle.length) {
25789 node.removeAttribute('style');
25793 this.iterateChildren(node, this.cleanTableWidths);
25801 domToHTML : function(currentElement, depth, nopadtext) {
25803 depth = depth || 0;
25804 nopadtext = nopadtext || false;
25806 if (!currentElement) {
25807 return this.domToHTML(this.doc.body);
25810 //Roo.log(currentElement);
25812 var allText = false;
25813 var nodeName = currentElement.nodeName;
25814 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25816 if (nodeName == '#text') {
25818 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25823 if (nodeName != 'BODY') {
25826 // Prints the node tagName, such as <A>, <IMG>, etc
25829 for(i = 0; i < currentElement.attributes.length;i++) {
25831 var aname = currentElement.attributes.item(i).name;
25832 if (!currentElement.attributes.item(i).value.length) {
25835 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25838 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25847 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25850 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25855 // Traverse the tree
25857 var currentElementChild = currentElement.childNodes.item(i);
25858 var allText = true;
25859 var innerHTML = '';
25861 while (currentElementChild) {
25862 // Formatting code (indent the tree so it looks nice on the screen)
25863 var nopad = nopadtext;
25864 if (lastnode == 'SPAN') {
25868 if (currentElementChild.nodeName == '#text') {
25869 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25870 toadd = nopadtext ? toadd : toadd.trim();
25871 if (!nopad && toadd.length > 80) {
25872 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25874 innerHTML += toadd;
25877 currentElementChild = currentElement.childNodes.item(i);
25883 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25885 // Recursively traverse the tree structure of the child node
25886 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25887 lastnode = currentElementChild.nodeName;
25889 currentElementChild=currentElement.childNodes.item(i);
25895 // The remaining code is mostly for formatting the tree
25896 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25901 ret+= "</"+tagName+">";
25907 applyBlacklists : function()
25909 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25910 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25914 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25915 if (b.indexOf(tag) > -1) {
25918 this.white.push(tag);
25922 Roo.each(w, function(tag) {
25923 if (b.indexOf(tag) > -1) {
25926 if (this.white.indexOf(tag) > -1) {
25929 this.white.push(tag);
25934 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25935 if (w.indexOf(tag) > -1) {
25938 this.black.push(tag);
25942 Roo.each(b, function(tag) {
25943 if (w.indexOf(tag) > -1) {
25946 if (this.black.indexOf(tag) > -1) {
25949 this.black.push(tag);
25954 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25955 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25959 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25960 if (b.indexOf(tag) > -1) {
25963 this.cwhite.push(tag);
25967 Roo.each(w, function(tag) {
25968 if (b.indexOf(tag) > -1) {
25971 if (this.cwhite.indexOf(tag) > -1) {
25974 this.cwhite.push(tag);
25979 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25980 if (w.indexOf(tag) > -1) {
25983 this.cblack.push(tag);
25987 Roo.each(b, function(tag) {
25988 if (w.indexOf(tag) > -1) {
25991 if (this.cblack.indexOf(tag) > -1) {
25994 this.cblack.push(tag);
25999 setStylesheets : function(stylesheets)
26001 if(typeof(stylesheets) == 'string'){
26002 Roo.get(this.iframe.contentDocument.head).createChild({
26004 rel : 'stylesheet',
26013 Roo.each(stylesheets, function(s) {
26018 Roo.get(_this.iframe.contentDocument.head).createChild({
26020 rel : 'stylesheet',
26029 removeStylesheets : function()
26033 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26038 setStyle : function(style)
26040 Roo.get(this.iframe.contentDocument.head).createChild({
26049 // hide stuff that is not compatible
26063 * @event specialkey
26067 * @cfg {String} fieldClass @hide
26070 * @cfg {String} focusClass @hide
26073 * @cfg {String} autoCreate @hide
26076 * @cfg {String} inputType @hide
26079 * @cfg {String} invalidClass @hide
26082 * @cfg {String} invalidText @hide
26085 * @cfg {String} msgFx @hide
26088 * @cfg {String} validateOnBlur @hide
26092 Roo.HtmlEditorCore.white = [
26093 'area', 'br', 'img', 'input', 'hr', 'wbr',
26095 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26096 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26097 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26098 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26099 'table', 'ul', 'xmp',
26101 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26104 'dir', 'menu', 'ol', 'ul', 'dl',
26110 Roo.HtmlEditorCore.black = [
26111 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26113 'base', 'basefont', 'bgsound', 'blink', 'body',
26114 'frame', 'frameset', 'head', 'html', 'ilayer',
26115 'iframe', 'layer', 'link', 'meta', 'object',
26116 'script', 'style' ,'title', 'xml' // clean later..
26118 Roo.HtmlEditorCore.clean = [
26119 'script', 'style', 'title', 'xml'
26121 Roo.HtmlEditorCore.remove = [
26126 Roo.HtmlEditorCore.ablack = [
26130 Roo.HtmlEditorCore.aclean = [
26131 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26135 Roo.HtmlEditorCore.pwhite= [
26136 'http', 'https', 'mailto'
26139 // white listed style attributes.
26140 Roo.HtmlEditorCore.cwhite= [
26141 // 'text-align', /// default is to allow most things..
26147 // black listed style attributes.
26148 Roo.HtmlEditorCore.cblack= [
26149 // 'font-size' -- this can be set by the project
26153 Roo.HtmlEditorCore.swapCodes =[
26154 [ 8211, "–" ],
26155 [ 8212, "—" ],
26172 * @class Roo.bootstrap.HtmlEditor
26173 * @extends Roo.bootstrap.TextArea
26174 * Bootstrap HtmlEditor class
26177 * Create a new HtmlEditor
26178 * @param {Object} config The config object
26181 Roo.bootstrap.HtmlEditor = function(config){
26182 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26183 if (!this.toolbars) {
26184 this.toolbars = [];
26187 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26190 * @event initialize
26191 * Fires when the editor is fully initialized (including the iframe)
26192 * @param {HtmlEditor} this
26197 * Fires when the editor is first receives the focus. Any insertion must wait
26198 * until after this event.
26199 * @param {HtmlEditor} this
26203 * @event beforesync
26204 * Fires before the textarea is updated with content from the editor iframe. Return false
26205 * to cancel the sync.
26206 * @param {HtmlEditor} this
26207 * @param {String} html
26211 * @event beforepush
26212 * Fires before the iframe editor is updated with content from the textarea. Return false
26213 * to cancel the push.
26214 * @param {HtmlEditor} this
26215 * @param {String} html
26220 * Fires when the textarea is updated with content from the editor iframe.
26221 * @param {HtmlEditor} this
26222 * @param {String} html
26227 * Fires when the iframe editor is updated with content from the textarea.
26228 * @param {HtmlEditor} this
26229 * @param {String} html
26233 * @event editmodechange
26234 * Fires when the editor switches edit modes
26235 * @param {HtmlEditor} this
26236 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26238 editmodechange: true,
26240 * @event editorevent
26241 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26242 * @param {HtmlEditor} this
26246 * @event firstfocus
26247 * Fires when on first focus - needed by toolbars..
26248 * @param {HtmlEditor} this
26253 * Auto save the htmlEditor value as a file into Events
26254 * @param {HtmlEditor} this
26258 * @event savedpreview
26259 * preview the saved version of htmlEditor
26260 * @param {HtmlEditor} this
26267 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26271 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26276 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26281 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26286 * @cfg {Number} height (in pixels)
26290 * @cfg {Number} width (in pixels)
26295 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26298 stylesheets: false,
26303 // private properties
26304 validationEvent : false,
26306 initialized : false,
26309 onFocus : Roo.emptyFn,
26311 hideMode:'offsets',
26313 tbContainer : false,
26317 toolbarContainer :function() {
26318 return this.wrap.select('.x-html-editor-tb',true).first();
26322 * Protected method that will not generally be called directly. It
26323 * is called when the editor creates its toolbar. Override this method if you need to
26324 * add custom toolbar buttons.
26325 * @param {HtmlEditor} editor
26327 createToolbar : function(){
26328 Roo.log('renewing');
26329 Roo.log("create toolbars");
26331 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26332 this.toolbars[0].render(this.toolbarContainer());
26336 // if (!editor.toolbars || !editor.toolbars.length) {
26337 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26340 // for (var i =0 ; i < editor.toolbars.length;i++) {
26341 // editor.toolbars[i] = Roo.factory(
26342 // typeof(editor.toolbars[i]) == 'string' ?
26343 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26344 // Roo.bootstrap.HtmlEditor);
26345 // editor.toolbars[i].init(editor);
26351 onRender : function(ct, position)
26353 // Roo.log("Call onRender: " + this.xtype);
26355 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26357 this.wrap = this.inputEl().wrap({
26358 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26361 this.editorcore.onRender(ct, position);
26363 if (this.resizable) {
26364 this.resizeEl = new Roo.Resizable(this.wrap, {
26368 minHeight : this.height,
26369 height: this.height,
26370 handles : this.resizable,
26373 resize : function(r, w, h) {
26374 _t.onResize(w,h); // -something
26380 this.createToolbar(this);
26383 if(!this.width && this.resizable){
26384 this.setSize(this.wrap.getSize());
26386 if (this.resizeEl) {
26387 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26388 // should trigger onReize..
26394 onResize : function(w, h)
26396 Roo.log('resize: ' +w + ',' + h );
26397 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26401 if(this.inputEl() ){
26402 if(typeof w == 'number'){
26403 var aw = w - this.wrap.getFrameWidth('lr');
26404 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26407 if(typeof h == 'number'){
26408 var tbh = -11; // fixme it needs to tool bar size!
26409 for (var i =0; i < this.toolbars.length;i++) {
26410 // fixme - ask toolbars for heights?
26411 tbh += this.toolbars[i].el.getHeight();
26412 //if (this.toolbars[i].footer) {
26413 // tbh += this.toolbars[i].footer.el.getHeight();
26421 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26422 ah -= 5; // knock a few pixes off for look..
26423 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26427 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26428 this.editorcore.onResize(ew,eh);
26433 * Toggles the editor between standard and source edit mode.
26434 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26436 toggleSourceEdit : function(sourceEditMode)
26438 this.editorcore.toggleSourceEdit(sourceEditMode);
26440 if(this.editorcore.sourceEditMode){
26441 Roo.log('editor - showing textarea');
26444 // Roo.log(this.syncValue());
26446 this.inputEl().removeClass(['hide', 'x-hidden']);
26447 this.inputEl().dom.removeAttribute('tabIndex');
26448 this.inputEl().focus();
26450 Roo.log('editor - hiding textarea');
26452 // Roo.log(this.pushValue());
26455 this.inputEl().addClass(['hide', 'x-hidden']);
26456 this.inputEl().dom.setAttribute('tabIndex', -1);
26457 //this.deferFocus();
26460 if(this.resizable){
26461 this.setSize(this.wrap.getSize());
26464 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26467 // private (for BoxComponent)
26468 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26470 // private (for BoxComponent)
26471 getResizeEl : function(){
26475 // private (for BoxComponent)
26476 getPositionEl : function(){
26481 initEvents : function(){
26482 this.originalValue = this.getValue();
26486 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26489 // markInvalid : Roo.emptyFn,
26491 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26494 // clearInvalid : Roo.emptyFn,
26496 setValue : function(v){
26497 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26498 this.editorcore.pushValue();
26503 deferFocus : function(){
26504 this.focus.defer(10, this);
26508 focus : function(){
26509 this.editorcore.focus();
26515 onDestroy : function(){
26521 for (var i =0; i < this.toolbars.length;i++) {
26522 // fixme - ask toolbars for heights?
26523 this.toolbars[i].onDestroy();
26526 this.wrap.dom.innerHTML = '';
26527 this.wrap.remove();
26532 onFirstFocus : function(){
26533 //Roo.log("onFirstFocus");
26534 this.editorcore.onFirstFocus();
26535 for (var i =0; i < this.toolbars.length;i++) {
26536 this.toolbars[i].onFirstFocus();
26542 syncValue : function()
26544 this.editorcore.syncValue();
26547 pushValue : function()
26549 this.editorcore.pushValue();
26553 // hide stuff that is not compatible
26567 * @event specialkey
26571 * @cfg {String} fieldClass @hide
26574 * @cfg {String} focusClass @hide
26577 * @cfg {String} autoCreate @hide
26580 * @cfg {String} inputType @hide
26584 * @cfg {String} invalidText @hide
26587 * @cfg {String} msgFx @hide
26590 * @cfg {String} validateOnBlur @hide
26599 Roo.namespace('Roo.bootstrap.htmleditor');
26601 * @class Roo.bootstrap.HtmlEditorToolbar1
26607 new Roo.bootstrap.HtmlEditor({
26610 new Roo.bootstrap.HtmlEditorToolbar1({
26611 disable : { fonts: 1 , format: 1, ..., ... , ...],
26617 * @cfg {Object} disable List of elements to disable..
26618 * @cfg {Array} btns List of additional buttons.
26622 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26625 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26628 Roo.apply(this, config);
26630 // default disabled, based on 'good practice'..
26631 this.disable = this.disable || {};
26632 Roo.applyIf(this.disable, {
26635 specialElements : true
26637 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26639 this.editor = config.editor;
26640 this.editorcore = config.editor.editorcore;
26642 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26644 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26645 // dont call parent... till later.
26647 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26652 editorcore : false,
26657 "h1","h2","h3","h4","h5","h6",
26659 "abbr", "acronym", "address", "cite", "samp", "var",
26663 onRender : function(ct, position)
26665 // Roo.log("Call onRender: " + this.xtype);
26667 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26669 this.el.dom.style.marginBottom = '0';
26671 var editorcore = this.editorcore;
26672 var editor= this.editor;
26675 var btn = function(id,cmd , toggle, handler, html){
26677 var event = toggle ? 'toggle' : 'click';
26682 xns: Roo.bootstrap,
26686 enableToggle:toggle !== false,
26688 pressed : toggle ? false : null,
26691 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26692 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26698 // var cb_box = function...
26703 xns: Roo.bootstrap,
26708 xns: Roo.bootstrap,
26712 Roo.each(this.formats, function(f) {
26713 style.menu.items.push({
26715 xns: Roo.bootstrap,
26716 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26721 editorcore.insertTag(this.tagname);
26728 children.push(style);
26730 btn('bold',false,true);
26731 btn('italic',false,true);
26732 btn('align-left', 'justifyleft',true);
26733 btn('align-center', 'justifycenter',true);
26734 btn('align-right' , 'justifyright',true);
26735 btn('link', false, false, function(btn) {
26736 //Roo.log("create link?");
26737 var url = prompt(this.createLinkText, this.defaultLinkValue);
26738 if(url && url != 'http:/'+'/'){
26739 this.editorcore.relayCmd('createlink', url);
26742 btn('list','insertunorderedlist',true);
26743 btn('pencil', false,true, function(btn){
26745 this.toggleSourceEdit(btn.pressed);
26748 if (this.editor.btns.length > 0) {
26749 for (var i = 0; i<this.editor.btns.length; i++) {
26750 children.push(this.editor.btns[i]);
26758 xns: Roo.bootstrap,
26763 xns: Roo.bootstrap,
26768 cog.menu.items.push({
26770 xns: Roo.bootstrap,
26771 html : Clean styles,
26776 editorcore.insertTag(this.tagname);
26785 this.xtype = 'NavSimplebar';
26787 for(var i=0;i< children.length;i++) {
26789 this.buttons.add(this.addxtypeChild(children[i]));
26793 editor.on('editorevent', this.updateToolbar, this);
26795 onBtnClick : function(id)
26797 this.editorcore.relayCmd(id);
26798 this.editorcore.focus();
26802 * Protected method that will not generally be called directly. It triggers
26803 * a toolbar update by reading the markup state of the current selection in the editor.
26805 updateToolbar: function(){
26807 if(!this.editorcore.activated){
26808 this.editor.onFirstFocus(); // is this neeed?
26812 var btns = this.buttons;
26813 var doc = this.editorcore.doc;
26814 btns.get('bold').setActive(doc.queryCommandState('bold'));
26815 btns.get('italic').setActive(doc.queryCommandState('italic'));
26816 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26818 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26819 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26820 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26822 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26823 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26826 var ans = this.editorcore.getAllAncestors();
26827 if (this.formatCombo) {
26830 var store = this.formatCombo.store;
26831 this.formatCombo.setValue("");
26832 for (var i =0; i < ans.length;i++) {
26833 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26835 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26843 // hides menus... - so this cant be on a menu...
26844 Roo.bootstrap.MenuMgr.hideAll();
26846 Roo.bootstrap.MenuMgr.hideAll();
26847 //this.editorsyncValue();
26849 onFirstFocus: function() {
26850 this.buttons.each(function(item){
26854 toggleSourceEdit : function(sourceEditMode){
26857 if(sourceEditMode){
26858 Roo.log("disabling buttons");
26859 this.buttons.each( function(item){
26860 if(item.cmd != 'pencil'){
26866 Roo.log("enabling buttons");
26867 if(this.editorcore.initialized){
26868 this.buttons.each( function(item){
26874 Roo.log("calling toggole on editor");
26875 // tell the editor that it's been pressed..
26876 this.editor.toggleSourceEdit(sourceEditMode);
26890 * @class Roo.bootstrap.Markdown
26891 * @extends Roo.bootstrap.TextArea
26892 * Bootstrap Showdown editable area
26893 * @cfg {string} content
26896 * Create a new Showdown
26899 Roo.bootstrap.Markdown = function(config){
26900 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26904 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26908 initEvents : function()
26911 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26912 this.markdownEl = this.el.createChild({
26913 cls : 'roo-markdown-area'
26915 this.inputEl().addClass('d-none');
26916 if (this.getValue() == '') {
26917 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26920 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26922 this.markdownEl.on('click', this.toggleTextEdit, this);
26923 this.on('blur', this.toggleTextEdit, this);
26924 this.on('specialkey', this.resizeTextArea, this);
26927 toggleTextEdit : function()
26929 var sh = this.markdownEl.getHeight();
26930 this.inputEl().addClass('d-none');
26931 this.markdownEl.addClass('d-none');
26932 if (!this.editing) {
26934 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26935 this.inputEl().removeClass('d-none');
26936 this.inputEl().focus();
26937 this.editing = true;
26940 // show showdown...
26941 this.updateMarkdown();
26942 this.markdownEl.removeClass('d-none');
26943 this.editing = false;
26946 updateMarkdown : function()
26948 if (this.getValue() == '') {
26949 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26953 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26956 resizeTextArea: function () {
26959 Roo.log([sh, this.getValue().split("\n").length * 30]);
26960 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26962 setValue : function(val)
26964 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26965 if (!this.editing) {
26966 this.updateMarkdown();
26972 if (!this.editing) {
26973 this.toggleTextEdit();
26981 * @class Roo.bootstrap.Table.AbstractSelectionModel
26982 * @extends Roo.util.Observable
26983 * Abstract base class for grid SelectionModels. It provides the interface that should be
26984 * implemented by descendant classes. This class should not be directly instantiated.
26987 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26988 this.locked = false;
26989 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26993 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26994 /** @ignore Called by the grid automatically. Do not call directly. */
26995 init : function(grid){
27001 * Locks the selections.
27004 this.locked = true;
27008 * Unlocks the selections.
27010 unlock : function(){
27011 this.locked = false;
27015 * Returns true if the selections are locked.
27016 * @return {Boolean}
27018 isLocked : function(){
27019 return this.locked;
27023 initEvents : function ()
27029 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27030 * @class Roo.bootstrap.Table.RowSelectionModel
27031 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27032 * It supports multiple selections and keyboard selection/navigation.
27034 * @param {Object} config
27037 Roo.bootstrap.Table.RowSelectionModel = function(config){
27038 Roo.apply(this, config);
27039 this.selections = new Roo.util.MixedCollection(false, function(o){
27044 this.lastActive = false;
27048 * @event selectionchange
27049 * Fires when the selection changes
27050 * @param {SelectionModel} this
27052 "selectionchange" : true,
27054 * @event afterselectionchange
27055 * Fires after the selection changes (eg. by key press or clicking)
27056 * @param {SelectionModel} this
27058 "afterselectionchange" : true,
27060 * @event beforerowselect
27061 * Fires when a row is selected being selected, return false to cancel.
27062 * @param {SelectionModel} this
27063 * @param {Number} rowIndex The selected index
27064 * @param {Boolean} keepExisting False if other selections will be cleared
27066 "beforerowselect" : true,
27069 * Fires when a row is selected.
27070 * @param {SelectionModel} this
27071 * @param {Number} rowIndex The selected index
27072 * @param {Roo.data.Record} r The record
27074 "rowselect" : true,
27076 * @event rowdeselect
27077 * Fires when a row is deselected.
27078 * @param {SelectionModel} this
27079 * @param {Number} rowIndex The selected index
27081 "rowdeselect" : true
27083 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27084 this.locked = false;
27087 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27089 * @cfg {Boolean} singleSelect
27090 * True to allow selection of only one row at a time (defaults to false)
27092 singleSelect : false,
27095 initEvents : function()
27098 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27099 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27100 //}else{ // allow click to work like normal
27101 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27103 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27104 this.grid.on("rowclick", this.handleMouseDown, this);
27106 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27107 "up" : function(e){
27109 this.selectPrevious(e.shiftKey);
27110 }else if(this.last !== false && this.lastActive !== false){
27111 var last = this.last;
27112 this.selectRange(this.last, this.lastActive-1);
27113 this.grid.getView().focusRow(this.lastActive);
27114 if(last !== false){
27118 this.selectFirstRow();
27120 this.fireEvent("afterselectionchange", this);
27122 "down" : function(e){
27124 this.selectNext(e.shiftKey);
27125 }else if(this.last !== false && this.lastActive !== false){
27126 var last = this.last;
27127 this.selectRange(this.last, this.lastActive+1);
27128 this.grid.getView().focusRow(this.lastActive);
27129 if(last !== false){
27133 this.selectFirstRow();
27135 this.fireEvent("afterselectionchange", this);
27139 this.grid.store.on('load', function(){
27140 this.selections.clear();
27143 var view = this.grid.view;
27144 view.on("refresh", this.onRefresh, this);
27145 view.on("rowupdated", this.onRowUpdated, this);
27146 view.on("rowremoved", this.onRemove, this);
27151 onRefresh : function()
27153 var ds = this.grid.store, i, v = this.grid.view;
27154 var s = this.selections;
27155 s.each(function(r){
27156 if((i = ds.indexOfId(r.id)) != -1){
27165 onRemove : function(v, index, r){
27166 this.selections.remove(r);
27170 onRowUpdated : function(v, index, r){
27171 if(this.isSelected(r)){
27172 v.onRowSelect(index);
27178 * @param {Array} records The records to select
27179 * @param {Boolean} keepExisting (optional) True to keep existing selections
27181 selectRecords : function(records, keepExisting)
27184 this.clearSelections();
27186 var ds = this.grid.store;
27187 for(var i = 0, len = records.length; i < len; i++){
27188 this.selectRow(ds.indexOf(records[i]), true);
27193 * Gets the number of selected rows.
27196 getCount : function(){
27197 return this.selections.length;
27201 * Selects the first row in the grid.
27203 selectFirstRow : function(){
27208 * Select the last row.
27209 * @param {Boolean} keepExisting (optional) True to keep existing selections
27211 selectLastRow : function(keepExisting){
27212 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27213 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27217 * Selects the row immediately following the last selected row.
27218 * @param {Boolean} keepExisting (optional) True to keep existing selections
27220 selectNext : function(keepExisting)
27222 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27223 this.selectRow(this.last+1, keepExisting);
27224 this.grid.getView().focusRow(this.last);
27229 * Selects the row that precedes the last selected row.
27230 * @param {Boolean} keepExisting (optional) True to keep existing selections
27232 selectPrevious : function(keepExisting){
27234 this.selectRow(this.last-1, keepExisting);
27235 this.grid.getView().focusRow(this.last);
27240 * Returns the selected records
27241 * @return {Array} Array of selected records
27243 getSelections : function(){
27244 return [].concat(this.selections.items);
27248 * Returns the first selected record.
27251 getSelected : function(){
27252 return this.selections.itemAt(0);
27257 * Clears all selections.
27259 clearSelections : function(fast)
27265 var ds = this.grid.store;
27266 var s = this.selections;
27267 s.each(function(r){
27268 this.deselectRow(ds.indexOfId(r.id));
27272 this.selections.clear();
27279 * Selects all rows.
27281 selectAll : function(){
27285 this.selections.clear();
27286 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27287 this.selectRow(i, true);
27292 * Returns True if there is a selection.
27293 * @return {Boolean}
27295 hasSelection : function(){
27296 return this.selections.length > 0;
27300 * Returns True if the specified row is selected.
27301 * @param {Number/Record} record The record or index of the record to check
27302 * @return {Boolean}
27304 isSelected : function(index){
27305 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27306 return (r && this.selections.key(r.id) ? true : false);
27310 * Returns True if the specified record id is selected.
27311 * @param {String} id The id of record to check
27312 * @return {Boolean}
27314 isIdSelected : function(id){
27315 return (this.selections.key(id) ? true : false);
27320 handleMouseDBClick : function(e, t){
27324 handleMouseDown : function(e, t)
27326 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27327 if(this.isLocked() || rowIndex < 0 ){
27330 if(e.shiftKey && this.last !== false){
27331 var last = this.last;
27332 this.selectRange(last, rowIndex, e.ctrlKey);
27333 this.last = last; // reset the last
27337 var isSelected = this.isSelected(rowIndex);
27338 //Roo.log("select row:" + rowIndex);
27340 this.deselectRow(rowIndex);
27342 this.selectRow(rowIndex, true);
27346 if(e.button !== 0 && isSelected){
27347 alert('rowIndex 2: ' + rowIndex);
27348 view.focusRow(rowIndex);
27349 }else if(e.ctrlKey && isSelected){
27350 this.deselectRow(rowIndex);
27351 }else if(!isSelected){
27352 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27353 view.focusRow(rowIndex);
27357 this.fireEvent("afterselectionchange", this);
27360 handleDragableRowClick : function(grid, rowIndex, e)
27362 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27363 this.selectRow(rowIndex, false);
27364 grid.view.focusRow(rowIndex);
27365 this.fireEvent("afterselectionchange", this);
27370 * Selects multiple rows.
27371 * @param {Array} rows Array of the indexes of the row to select
27372 * @param {Boolean} keepExisting (optional) True to keep existing selections
27374 selectRows : function(rows, keepExisting){
27376 this.clearSelections();
27378 for(var i = 0, len = rows.length; i < len; i++){
27379 this.selectRow(rows[i], true);
27384 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27385 * @param {Number} startRow The index of the first row in the range
27386 * @param {Number} endRow The index of the last row in the range
27387 * @param {Boolean} keepExisting (optional) True to retain existing selections
27389 selectRange : function(startRow, endRow, keepExisting){
27394 this.clearSelections();
27396 if(startRow <= endRow){
27397 for(var i = startRow; i <= endRow; i++){
27398 this.selectRow(i, true);
27401 for(var i = startRow; i >= endRow; i--){
27402 this.selectRow(i, true);
27408 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27409 * @param {Number} startRow The index of the first row in the range
27410 * @param {Number} endRow The index of the last row in the range
27412 deselectRange : function(startRow, endRow, preventViewNotify){
27416 for(var i = startRow; i <= endRow; i++){
27417 this.deselectRow(i, preventViewNotify);
27423 * @param {Number} row The index of the row to select
27424 * @param {Boolean} keepExisting (optional) True to keep existing selections
27426 selectRow : function(index, keepExisting, preventViewNotify)
27428 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27431 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27432 if(!keepExisting || this.singleSelect){
27433 this.clearSelections();
27436 var r = this.grid.store.getAt(index);
27437 //console.log('selectRow - record id :' + r.id);
27439 this.selections.add(r);
27440 this.last = this.lastActive = index;
27441 if(!preventViewNotify){
27442 var proxy = new Roo.Element(
27443 this.grid.getRowDom(index)
27445 proxy.addClass('bg-info info');
27447 this.fireEvent("rowselect", this, index, r);
27448 this.fireEvent("selectionchange", this);
27454 * @param {Number} row The index of the row to deselect
27456 deselectRow : function(index, preventViewNotify)
27461 if(this.last == index){
27464 if(this.lastActive == index){
27465 this.lastActive = false;
27468 var r = this.grid.store.getAt(index);
27473 this.selections.remove(r);
27474 //.console.log('deselectRow - record id :' + r.id);
27475 if(!preventViewNotify){
27477 var proxy = new Roo.Element(
27478 this.grid.getRowDom(index)
27480 proxy.removeClass('bg-info info');
27482 this.fireEvent("rowdeselect", this, index);
27483 this.fireEvent("selectionchange", this);
27487 restoreLast : function(){
27489 this.last = this._last;
27494 acceptsNav : function(row, col, cm){
27495 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27499 onEditorKey : function(field, e){
27500 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27505 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27507 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27509 }else if(k == e.ENTER && !e.ctrlKey){
27513 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27515 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27517 }else if(k == e.ESC){
27521 g.startEditing(newCell[0], newCell[1]);
27527 * Ext JS Library 1.1.1
27528 * Copyright(c) 2006-2007, Ext JS, LLC.
27530 * Originally Released Under LGPL - original licence link has changed is not relivant.
27533 * <script type="text/javascript">
27537 * @class Roo.bootstrap.PagingToolbar
27538 * @extends Roo.bootstrap.NavSimplebar
27539 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27541 * Create a new PagingToolbar
27542 * @param {Object} config The config object
27543 * @param {Roo.data.Store} store
27545 Roo.bootstrap.PagingToolbar = function(config)
27547 // old args format still supported... - xtype is prefered..
27548 // created from xtype...
27550 this.ds = config.dataSource;
27552 if (config.store && !this.ds) {
27553 this.store= Roo.factory(config.store, Roo.data);
27554 this.ds = this.store;
27555 this.ds.xmodule = this.xmodule || false;
27558 this.toolbarItems = [];
27559 if (config.items) {
27560 this.toolbarItems = config.items;
27563 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27568 this.bind(this.ds);
27571 if (Roo.bootstrap.version == 4) {
27572 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27574 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27579 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27581 * @cfg {Roo.data.Store} dataSource
27582 * The underlying data store providing the paged data
27585 * @cfg {String/HTMLElement/Element} container
27586 * container The id or element that will contain the toolbar
27589 * @cfg {Boolean} displayInfo
27590 * True to display the displayMsg (defaults to false)
27593 * @cfg {Number} pageSize
27594 * The number of records to display per page (defaults to 20)
27598 * @cfg {String} displayMsg
27599 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27601 displayMsg : 'Displaying {0} - {1} of {2}',
27603 * @cfg {String} emptyMsg
27604 * The message to display when no records are found (defaults to "No data to display")
27606 emptyMsg : 'No data to display',
27608 * Customizable piece of the default paging text (defaults to "Page")
27611 beforePageText : "Page",
27613 * Customizable piece of the default paging text (defaults to "of %0")
27616 afterPageText : "of {0}",
27618 * Customizable piece of the default paging text (defaults to "First Page")
27621 firstText : "First Page",
27623 * Customizable piece of the default paging text (defaults to "Previous Page")
27626 prevText : "Previous Page",
27628 * Customizable piece of the default paging text (defaults to "Next Page")
27631 nextText : "Next Page",
27633 * Customizable piece of the default paging text (defaults to "Last Page")
27636 lastText : "Last Page",
27638 * Customizable piece of the default paging text (defaults to "Refresh")
27641 refreshText : "Refresh",
27645 onRender : function(ct, position)
27647 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27648 this.navgroup.parentId = this.id;
27649 this.navgroup.onRender(this.el, null);
27650 // add the buttons to the navgroup
27652 if(this.displayInfo){
27653 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27654 this.displayEl = this.el.select('.x-paging-info', true).first();
27655 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27656 // this.displayEl = navel.el.select('span',true).first();
27662 Roo.each(_this.buttons, function(e){ // this might need to use render????
27663 Roo.factory(e).render(_this.el);
27667 Roo.each(_this.toolbarItems, function(e) {
27668 _this.navgroup.addItem(e);
27672 this.first = this.navgroup.addItem({
27673 tooltip: this.firstText,
27674 cls: "prev btn-outline-secondary",
27675 html : ' <i class="fa fa-step-backward"></i>',
27677 preventDefault: true,
27678 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27681 this.prev = this.navgroup.addItem({
27682 tooltip: this.prevText,
27683 cls: "prev btn-outline-secondary",
27684 html : ' <i class="fa fa-backward"></i>',
27686 preventDefault: true,
27687 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27689 //this.addSeparator();
27692 var field = this.navgroup.addItem( {
27694 cls : 'x-paging-position btn-outline-secondary',
27696 html : this.beforePageText +
27697 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27698 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27701 this.field = field.el.select('input', true).first();
27702 this.field.on("keydown", this.onPagingKeydown, this);
27703 this.field.on("focus", function(){this.dom.select();});
27706 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27707 //this.field.setHeight(18);
27708 //this.addSeparator();
27709 this.next = this.navgroup.addItem({
27710 tooltip: this.nextText,
27711 cls: "next btn-outline-secondary",
27712 html : ' <i class="fa fa-forward"></i>',
27714 preventDefault: true,
27715 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27717 this.last = this.navgroup.addItem({
27718 tooltip: this.lastText,
27719 html : ' <i class="fa fa-step-forward"></i>',
27720 cls: "next btn-outline-secondary",
27722 preventDefault: true,
27723 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27725 //this.addSeparator();
27726 this.loading = this.navgroup.addItem({
27727 tooltip: this.refreshText,
27728 cls: "btn-outline-secondary",
27729 html : ' <i class="fa fa-refresh"></i>',
27730 preventDefault: true,
27731 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27737 updateInfo : function(){
27738 if(this.displayEl){
27739 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27740 var msg = count == 0 ?
27744 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27746 this.displayEl.update(msg);
27751 onLoad : function(ds, r, o)
27753 this.cursor = o.params && o.params.start ? o.params.start : 0;
27755 var d = this.getPageData(),
27760 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27761 this.field.dom.value = ap;
27762 this.first.setDisabled(ap == 1);
27763 this.prev.setDisabled(ap == 1);
27764 this.next.setDisabled(ap == ps);
27765 this.last.setDisabled(ap == ps);
27766 this.loading.enable();
27771 getPageData : function(){
27772 var total = this.ds.getTotalCount();
27775 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27776 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27781 onLoadError : function(){
27782 this.loading.enable();
27786 onPagingKeydown : function(e){
27787 var k = e.getKey();
27788 var d = this.getPageData();
27790 var v = this.field.dom.value, pageNum;
27791 if(!v || isNaN(pageNum = parseInt(v, 10))){
27792 this.field.dom.value = d.activePage;
27795 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27796 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27799 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))
27801 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27802 this.field.dom.value = pageNum;
27803 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27806 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27808 var v = this.field.dom.value, pageNum;
27809 var increment = (e.shiftKey) ? 10 : 1;
27810 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27813 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27814 this.field.dom.value = d.activePage;
27817 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27819 this.field.dom.value = parseInt(v, 10) + increment;
27820 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27821 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27828 beforeLoad : function(){
27830 this.loading.disable();
27835 onClick : function(which){
27844 ds.load({params:{start: 0, limit: this.pageSize}});
27847 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27850 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27853 var total = ds.getTotalCount();
27854 var extra = total % this.pageSize;
27855 var lastStart = extra ? (total - extra) : total-this.pageSize;
27856 ds.load({params:{start: lastStart, limit: this.pageSize}});
27859 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27865 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27866 * @param {Roo.data.Store} store The data store to unbind
27868 unbind : function(ds){
27869 ds.un("beforeload", this.beforeLoad, this);
27870 ds.un("load", this.onLoad, this);
27871 ds.un("loadexception", this.onLoadError, this);
27872 ds.un("remove", this.updateInfo, this);
27873 ds.un("add", this.updateInfo, this);
27874 this.ds = undefined;
27878 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27879 * @param {Roo.data.Store} store The data store to bind
27881 bind : function(ds){
27882 ds.on("beforeload", this.beforeLoad, this);
27883 ds.on("load", this.onLoad, this);
27884 ds.on("loadexception", this.onLoadError, this);
27885 ds.on("remove", this.updateInfo, this);
27886 ds.on("add", this.updateInfo, this);
27897 * @class Roo.bootstrap.MessageBar
27898 * @extends Roo.bootstrap.Component
27899 * Bootstrap MessageBar class
27900 * @cfg {String} html contents of the MessageBar
27901 * @cfg {String} weight (info | success | warning | danger) default info
27902 * @cfg {String} beforeClass insert the bar before the given class
27903 * @cfg {Boolean} closable (true | false) default false
27904 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27907 * Create a new Element
27908 * @param {Object} config The config object
27911 Roo.bootstrap.MessageBar = function(config){
27912 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27915 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27921 beforeClass: 'bootstrap-sticky-wrap',
27923 getAutoCreate : function(){
27927 cls: 'alert alert-dismissable alert-' + this.weight,
27932 html: this.html || ''
27938 cfg.cls += ' alert-messages-fixed';
27952 onRender : function(ct, position)
27954 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27957 var cfg = Roo.apply({}, this.getAutoCreate());
27961 cfg.cls += ' ' + this.cls;
27964 cfg.style = this.style;
27966 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27968 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27971 this.el.select('>button.close').on('click', this.hide, this);
27977 if (!this.rendered) {
27983 this.fireEvent('show', this);
27989 if (!this.rendered) {
27995 this.fireEvent('hide', this);
27998 update : function()
28000 // var e = this.el.dom.firstChild;
28002 // if(this.closable){
28003 // e = e.nextSibling;
28006 // e.data = this.html || '';
28008 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28024 * @class Roo.bootstrap.Graph
28025 * @extends Roo.bootstrap.Component
28026 * Bootstrap Graph class
28030 @cfg {String} graphtype bar | vbar | pie
28031 @cfg {number} g_x coodinator | centre x (pie)
28032 @cfg {number} g_y coodinator | centre y (pie)
28033 @cfg {number} g_r radius (pie)
28034 @cfg {number} g_height height of the chart (respected by all elements in the set)
28035 @cfg {number} g_width width of the chart (respected by all elements in the set)
28036 @cfg {Object} title The title of the chart
28039 -opts (object) options for the chart
28041 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28042 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28044 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.
28045 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28047 o stretch (boolean)
28049 -opts (object) options for the pie
28052 o startAngle (number)
28053 o endAngle (number)
28057 * Create a new Input
28058 * @param {Object} config The config object
28061 Roo.bootstrap.Graph = function(config){
28062 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28068 * The img click event for the img.
28069 * @param {Roo.EventObject} e
28075 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28086 //g_colors: this.colors,
28093 getAutoCreate : function(){
28104 onRender : function(ct,position){
28107 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28109 if (typeof(Raphael) == 'undefined') {
28110 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28114 this.raphael = Raphael(this.el.dom);
28116 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28117 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28118 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28119 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28121 r.text(160, 10, "Single Series Chart").attr(txtattr);
28122 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28123 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28124 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28126 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28127 r.barchart(330, 10, 300, 220, data1);
28128 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28129 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28132 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28133 // r.barchart(30, 30, 560, 250, xdata, {
28134 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28135 // axis : "0 0 1 1",
28136 // axisxlabels : xdata
28137 // //yvalues : cols,
28140 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28142 // this.load(null,xdata,{
28143 // axis : "0 0 1 1",
28144 // axisxlabels : xdata
28149 load : function(graphtype,xdata,opts)
28151 this.raphael.clear();
28153 graphtype = this.graphtype;
28158 var r = this.raphael,
28159 fin = function () {
28160 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28162 fout = function () {
28163 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28165 pfin = function() {
28166 this.sector.stop();
28167 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28170 this.label[0].stop();
28171 this.label[0].attr({ r: 7.5 });
28172 this.label[1].attr({ "font-weight": 800 });
28175 pfout = function() {
28176 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28179 this.label[0].animate({ r: 5 }, 500, "bounce");
28180 this.label[1].attr({ "font-weight": 400 });
28186 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28189 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28192 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28193 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28195 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28202 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28207 setTitle: function(o)
28212 initEvents: function() {
28215 this.el.on('click', this.onClick, this);
28219 onClick : function(e)
28221 Roo.log('img onclick');
28222 this.fireEvent('click', this, e);
28234 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28237 * @class Roo.bootstrap.dash.NumberBox
28238 * @extends Roo.bootstrap.Component
28239 * Bootstrap NumberBox class
28240 * @cfg {String} headline Box headline
28241 * @cfg {String} content Box content
28242 * @cfg {String} icon Box icon
28243 * @cfg {String} footer Footer text
28244 * @cfg {String} fhref Footer href
28247 * Create a new NumberBox
28248 * @param {Object} config The config object
28252 Roo.bootstrap.dash.NumberBox = function(config){
28253 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28257 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28266 getAutoCreate : function(){
28270 cls : 'small-box ',
28278 cls : 'roo-headline',
28279 html : this.headline
28283 cls : 'roo-content',
28284 html : this.content
28298 cls : 'ion ' + this.icon
28307 cls : 'small-box-footer',
28308 href : this.fhref || '#',
28312 cfg.cn.push(footer);
28319 onRender : function(ct,position){
28320 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28327 setHeadline: function (value)
28329 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28332 setFooter: function (value, href)
28334 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28337 this.el.select('a.small-box-footer',true).first().attr('href', href);
28342 setContent: function (value)
28344 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28347 initEvents: function()
28361 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28364 * @class Roo.bootstrap.dash.TabBox
28365 * @extends Roo.bootstrap.Component
28366 * Bootstrap TabBox class
28367 * @cfg {String} title Title of the TabBox
28368 * @cfg {String} icon Icon of the TabBox
28369 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28370 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28373 * Create a new TabBox
28374 * @param {Object} config The config object
28378 Roo.bootstrap.dash.TabBox = function(config){
28379 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28384 * When a pane is added
28385 * @param {Roo.bootstrap.dash.TabPane} pane
28389 * @event activatepane
28390 * When a pane is activated
28391 * @param {Roo.bootstrap.dash.TabPane} pane
28393 "activatepane" : true
28401 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28406 tabScrollable : false,
28408 getChildContainer : function()
28410 return this.el.select('.tab-content', true).first();
28413 getAutoCreate : function(){
28417 cls: 'pull-left header',
28425 cls: 'fa ' + this.icon
28431 cls: 'nav nav-tabs pull-right',
28437 if(this.tabScrollable){
28444 cls: 'nav nav-tabs pull-right',
28455 cls: 'nav-tabs-custom',
28460 cls: 'tab-content no-padding',
28468 initEvents : function()
28470 //Roo.log('add add pane handler');
28471 this.on('addpane', this.onAddPane, this);
28474 * Updates the box title
28475 * @param {String} html to set the title to.
28477 setTitle : function(value)
28479 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28481 onAddPane : function(pane)
28483 this.panes.push(pane);
28484 //Roo.log('addpane');
28486 // tabs are rendere left to right..
28487 if(!this.showtabs){
28491 var ctr = this.el.select('.nav-tabs', true).first();
28494 var existing = ctr.select('.nav-tab',true);
28495 var qty = existing.getCount();;
28498 var tab = ctr.createChild({
28500 cls : 'nav-tab' + (qty ? '' : ' active'),
28508 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28511 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28513 pane.el.addClass('active');
28518 onTabClick : function(ev,un,ob,pane)
28520 //Roo.log('tab - prev default');
28521 ev.preventDefault();
28524 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28525 pane.tab.addClass('active');
28526 //Roo.log(pane.title);
28527 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28528 // technically we should have a deactivate event.. but maybe add later.
28529 // and it should not de-activate the selected tab...
28530 this.fireEvent('activatepane', pane);
28531 pane.el.addClass('active');
28532 pane.fireEvent('activate');
28537 getActivePane : function()
28540 Roo.each(this.panes, function(p) {
28541 if(p.el.hasClass('active')){
28562 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28564 * @class Roo.bootstrap.TabPane
28565 * @extends Roo.bootstrap.Component
28566 * Bootstrap TabPane class
28567 * @cfg {Boolean} active (false | true) Default false
28568 * @cfg {String} title title of panel
28572 * Create a new TabPane
28573 * @param {Object} config The config object
28576 Roo.bootstrap.dash.TabPane = function(config){
28577 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28583 * When a pane is activated
28584 * @param {Roo.bootstrap.dash.TabPane} pane
28591 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28596 // the tabBox that this is attached to.
28599 getAutoCreate : function()
28607 cfg.cls += ' active';
28612 initEvents : function()
28614 //Roo.log('trigger add pane handler');
28615 this.parent().fireEvent('addpane', this)
28619 * Updates the tab title
28620 * @param {String} html to set the title to.
28622 setTitle: function(str)
28628 this.tab.select('a', true).first().dom.innerHTML = str;
28645 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28648 * @class Roo.bootstrap.menu.Menu
28649 * @extends Roo.bootstrap.Component
28650 * Bootstrap Menu class - container for Menu
28651 * @cfg {String} html Text of the menu
28652 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28653 * @cfg {String} icon Font awesome icon
28654 * @cfg {String} pos Menu align to (top | bottom) default bottom
28658 * Create a new Menu
28659 * @param {Object} config The config object
28663 Roo.bootstrap.menu.Menu = function(config){
28664 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28668 * @event beforeshow
28669 * Fires before this menu is displayed
28670 * @param {Roo.bootstrap.menu.Menu} this
28674 * @event beforehide
28675 * Fires before this menu is hidden
28676 * @param {Roo.bootstrap.menu.Menu} this
28681 * Fires after this menu is displayed
28682 * @param {Roo.bootstrap.menu.Menu} this
28687 * Fires after this menu is hidden
28688 * @param {Roo.bootstrap.menu.Menu} this
28693 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28694 * @param {Roo.bootstrap.menu.Menu} this
28695 * @param {Roo.EventObject} e
28702 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28706 weight : 'default',
28711 getChildContainer : function() {
28712 if(this.isSubMenu){
28716 return this.el.select('ul.dropdown-menu', true).first();
28719 getAutoCreate : function()
28724 cls : 'roo-menu-text',
28732 cls : 'fa ' + this.icon
28743 cls : 'dropdown-button btn btn-' + this.weight,
28748 cls : 'dropdown-toggle btn btn-' + this.weight,
28758 cls : 'dropdown-menu'
28764 if(this.pos == 'top'){
28765 cfg.cls += ' dropup';
28768 if(this.isSubMenu){
28771 cls : 'dropdown-menu'
28778 onRender : function(ct, position)
28780 this.isSubMenu = ct.hasClass('dropdown-submenu');
28782 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28785 initEvents : function()
28787 if(this.isSubMenu){
28791 this.hidden = true;
28793 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28794 this.triggerEl.on('click', this.onTriggerPress, this);
28796 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28797 this.buttonEl.on('click', this.onClick, this);
28803 if(this.isSubMenu){
28807 return this.el.select('ul.dropdown-menu', true).first();
28810 onClick : function(e)
28812 this.fireEvent("click", this, e);
28815 onTriggerPress : function(e)
28817 if (this.isVisible()) {
28824 isVisible : function(){
28825 return !this.hidden;
28830 this.fireEvent("beforeshow", this);
28832 this.hidden = false;
28833 this.el.addClass('open');
28835 Roo.get(document).on("mouseup", this.onMouseUp, this);
28837 this.fireEvent("show", this);
28844 this.fireEvent("beforehide", this);
28846 this.hidden = true;
28847 this.el.removeClass('open');
28849 Roo.get(document).un("mouseup", this.onMouseUp);
28851 this.fireEvent("hide", this);
28854 onMouseUp : function()
28868 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28871 * @class Roo.bootstrap.menu.Item
28872 * @extends Roo.bootstrap.Component
28873 * Bootstrap MenuItem class
28874 * @cfg {Boolean} submenu (true | false) default false
28875 * @cfg {String} html text of the item
28876 * @cfg {String} href the link
28877 * @cfg {Boolean} disable (true | false) default false
28878 * @cfg {Boolean} preventDefault (true | false) default true
28879 * @cfg {String} icon Font awesome icon
28880 * @cfg {String} pos Submenu align to (left | right) default right
28884 * Create a new Item
28885 * @param {Object} config The config object
28889 Roo.bootstrap.menu.Item = function(config){
28890 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28894 * Fires when the mouse is hovering over this menu
28895 * @param {Roo.bootstrap.menu.Item} this
28896 * @param {Roo.EventObject} e
28901 * Fires when the mouse exits this menu
28902 * @param {Roo.bootstrap.menu.Item} this
28903 * @param {Roo.EventObject} e
28909 * The raw click event for the entire grid.
28910 * @param {Roo.EventObject} e
28916 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28921 preventDefault: true,
28926 getAutoCreate : function()
28931 cls : 'roo-menu-item-text',
28939 cls : 'fa ' + this.icon
28948 href : this.href || '#',
28955 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28959 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28961 if(this.pos == 'left'){
28962 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28969 initEvents : function()
28971 this.el.on('mouseover', this.onMouseOver, this);
28972 this.el.on('mouseout', this.onMouseOut, this);
28974 this.el.select('a', true).first().on('click', this.onClick, this);
28978 onClick : function(e)
28980 if(this.preventDefault){
28981 e.preventDefault();
28984 this.fireEvent("click", this, e);
28987 onMouseOver : function(e)
28989 if(this.submenu && this.pos == 'left'){
28990 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28993 this.fireEvent("mouseover", this, e);
28996 onMouseOut : function(e)
28998 this.fireEvent("mouseout", this, e);
29010 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29013 * @class Roo.bootstrap.menu.Separator
29014 * @extends Roo.bootstrap.Component
29015 * Bootstrap Separator class
29018 * Create a new Separator
29019 * @param {Object} config The config object
29023 Roo.bootstrap.menu.Separator = function(config){
29024 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29027 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29029 getAutoCreate : function(){
29032 cls: 'dropdown-divider divider'
29050 * @class Roo.bootstrap.Tooltip
29051 * Bootstrap Tooltip class
29052 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29053 * to determine which dom element triggers the tooltip.
29055 * It needs to add support for additional attributes like tooltip-position
29058 * Create a new Toolti
29059 * @param {Object} config The config object
29062 Roo.bootstrap.Tooltip = function(config){
29063 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29065 this.alignment = Roo.bootstrap.Tooltip.alignment;
29067 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29068 this.alignment = config.alignment;
29073 Roo.apply(Roo.bootstrap.Tooltip, {
29075 * @function init initialize tooltip monitoring.
29079 currentTip : false,
29080 currentRegion : false,
29086 Roo.get(document).on('mouseover', this.enter ,this);
29087 Roo.get(document).on('mouseout', this.leave, this);
29090 this.currentTip = new Roo.bootstrap.Tooltip();
29093 enter : function(ev)
29095 var dom = ev.getTarget();
29097 //Roo.log(['enter',dom]);
29098 var el = Roo.fly(dom);
29099 if (this.currentEl) {
29101 //Roo.log(this.currentEl);
29102 //Roo.log(this.currentEl.contains(dom));
29103 if (this.currentEl == el) {
29106 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29112 if (this.currentTip.el) {
29113 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29117 if(!el || el.dom == document){
29123 if (!el.attr('tooltip')) {
29124 pel = el.findParent("[tooltip]");
29126 bindEl = Roo.get(pel);
29132 // you can not look for children, as if el is the body.. then everythign is the child..
29133 if (!pel && !el.attr('tooltip')) { //
29134 if (!el.select("[tooltip]").elements.length) {
29137 // is the mouse over this child...?
29138 bindEl = el.select("[tooltip]").first();
29139 var xy = ev.getXY();
29140 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29141 //Roo.log("not in region.");
29144 //Roo.log("child element over..");
29147 this.currentEl = el;
29148 this.currentTip.bind(bindEl);
29149 this.currentRegion = Roo.lib.Region.getRegion(dom);
29150 this.currentTip.enter();
29153 leave : function(ev)
29155 var dom = ev.getTarget();
29156 //Roo.log(['leave',dom]);
29157 if (!this.currentEl) {
29162 if (dom != this.currentEl.dom) {
29165 var xy = ev.getXY();
29166 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29169 // only activate leave if mouse cursor is outside... bounding box..
29174 if (this.currentTip) {
29175 this.currentTip.leave();
29177 //Roo.log('clear currentEl');
29178 this.currentEl = false;
29183 'left' : ['r-l', [-2,0], 'right'],
29184 'right' : ['l-r', [2,0], 'left'],
29185 'bottom' : ['t-b', [0,2], 'top'],
29186 'top' : [ 'b-t', [0,-2], 'bottom']
29192 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29197 delay : null, // can be { show : 300 , hide: 500}
29201 hoverState : null, //???
29203 placement : 'bottom',
29207 getAutoCreate : function(){
29214 cls : 'tooltip-arrow arrow'
29217 cls : 'tooltip-inner'
29224 bind : function(el)
29229 initEvents : function()
29231 this.arrowEl = this.el.select('.arrow', true).first();
29232 this.innerEl = this.el.select('.tooltip-inner', true).first();
29235 enter : function () {
29237 if (this.timeout != null) {
29238 clearTimeout(this.timeout);
29241 this.hoverState = 'in';
29242 //Roo.log("enter - show");
29243 if (!this.delay || !this.delay.show) {
29248 this.timeout = setTimeout(function () {
29249 if (_t.hoverState == 'in') {
29252 }, this.delay.show);
29256 clearTimeout(this.timeout);
29258 this.hoverState = 'out';
29259 if (!this.delay || !this.delay.hide) {
29265 this.timeout = setTimeout(function () {
29266 //Roo.log("leave - timeout");
29268 if (_t.hoverState == 'out') {
29270 Roo.bootstrap.Tooltip.currentEl = false;
29275 show : function (msg)
29278 this.render(document.body);
29281 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29283 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29285 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29287 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29288 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29290 var placement = typeof this.placement == 'function' ?
29291 this.placement.call(this, this.el, on_el) :
29294 var autoToken = /\s?auto?\s?/i;
29295 var autoPlace = autoToken.test(placement);
29297 placement = placement.replace(autoToken, '') || 'top';
29301 //this.el.setXY([0,0]);
29303 //this.el.dom.style.display='block';
29305 //this.el.appendTo(on_el);
29307 var p = this.getPosition();
29308 var box = this.el.getBox();
29314 var align = this.alignment[placement];
29316 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29318 if(placement == 'top' || placement == 'bottom'){
29320 placement = 'right';
29323 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29324 placement = 'left';
29327 var scroll = Roo.select('body', true).first().getScroll();
29329 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29333 align = this.alignment[placement];
29335 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29339 var elems = document.getElementsByTagName('div');
29340 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29341 for (var i = 0; i < elems.length; i++) {
29342 var zindex = Number.parseInt(
29343 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29346 if (zindex > highest) {
29353 this.el.dom.style.zIndex = highest;
29355 this.el.alignTo(this.bindEl, align[0],align[1]);
29356 //var arrow = this.el.select('.arrow',true).first();
29357 //arrow.set(align[2],
29359 this.el.addClass(placement);
29360 this.el.addClass("bs-tooltip-"+ placement);
29362 this.el.addClass('in fade show');
29364 this.hoverState = null;
29366 if (this.el.hasClass('fade')) {
29381 //this.el.setXY([0,0]);
29382 this.el.removeClass(['show', 'in']);
29398 * @class Roo.bootstrap.LocationPicker
29399 * @extends Roo.bootstrap.Component
29400 * Bootstrap LocationPicker class
29401 * @cfg {Number} latitude Position when init default 0
29402 * @cfg {Number} longitude Position when init default 0
29403 * @cfg {Number} zoom default 15
29404 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29405 * @cfg {Boolean} mapTypeControl default false
29406 * @cfg {Boolean} disableDoubleClickZoom default false
29407 * @cfg {Boolean} scrollwheel default true
29408 * @cfg {Boolean} streetViewControl default false
29409 * @cfg {Number} radius default 0
29410 * @cfg {String} locationName
29411 * @cfg {Boolean} draggable default true
29412 * @cfg {Boolean} enableAutocomplete default false
29413 * @cfg {Boolean} enableReverseGeocode default true
29414 * @cfg {String} markerTitle
29417 * Create a new LocationPicker
29418 * @param {Object} config The config object
29422 Roo.bootstrap.LocationPicker = function(config){
29424 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29429 * Fires when the picker initialized.
29430 * @param {Roo.bootstrap.LocationPicker} this
29431 * @param {Google Location} location
29435 * @event positionchanged
29436 * Fires when the picker position changed.
29437 * @param {Roo.bootstrap.LocationPicker} this
29438 * @param {Google Location} location
29440 positionchanged : true,
29443 * Fires when the map resize.
29444 * @param {Roo.bootstrap.LocationPicker} this
29449 * Fires when the map show.
29450 * @param {Roo.bootstrap.LocationPicker} this
29455 * Fires when the map hide.
29456 * @param {Roo.bootstrap.LocationPicker} this
29461 * Fires when click the map.
29462 * @param {Roo.bootstrap.LocationPicker} this
29463 * @param {Map event} e
29467 * @event mapRightClick
29468 * Fires when right click the map.
29469 * @param {Roo.bootstrap.LocationPicker} this
29470 * @param {Map event} e
29472 mapRightClick : true,
29474 * @event markerClick
29475 * Fires when click the marker.
29476 * @param {Roo.bootstrap.LocationPicker} this
29477 * @param {Map event} e
29479 markerClick : true,
29481 * @event markerRightClick
29482 * Fires when right click the marker.
29483 * @param {Roo.bootstrap.LocationPicker} this
29484 * @param {Map event} e
29486 markerRightClick : true,
29488 * @event OverlayViewDraw
29489 * Fires when OverlayView Draw
29490 * @param {Roo.bootstrap.LocationPicker} this
29492 OverlayViewDraw : true,
29494 * @event OverlayViewOnAdd
29495 * Fires when OverlayView Draw
29496 * @param {Roo.bootstrap.LocationPicker} this
29498 OverlayViewOnAdd : true,
29500 * @event OverlayViewOnRemove
29501 * Fires when OverlayView Draw
29502 * @param {Roo.bootstrap.LocationPicker} this
29504 OverlayViewOnRemove : true,
29506 * @event OverlayViewShow
29507 * Fires when OverlayView Draw
29508 * @param {Roo.bootstrap.LocationPicker} this
29509 * @param {Pixel} cpx
29511 OverlayViewShow : true,
29513 * @event OverlayViewHide
29514 * Fires when OverlayView Draw
29515 * @param {Roo.bootstrap.LocationPicker} this
29517 OverlayViewHide : true,
29519 * @event loadexception
29520 * Fires when load google lib failed.
29521 * @param {Roo.bootstrap.LocationPicker} this
29523 loadexception : true
29528 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29530 gMapContext: false,
29536 mapTypeControl: false,
29537 disableDoubleClickZoom: false,
29539 streetViewControl: false,
29543 enableAutocomplete: false,
29544 enableReverseGeocode: true,
29547 getAutoCreate: function()
29552 cls: 'roo-location-picker'
29558 initEvents: function(ct, position)
29560 if(!this.el.getWidth() || this.isApplied()){
29564 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29569 initial: function()
29571 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29572 this.fireEvent('loadexception', this);
29576 if(!this.mapTypeId){
29577 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29580 this.gMapContext = this.GMapContext();
29582 this.initOverlayView();
29584 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29588 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29589 _this.setPosition(_this.gMapContext.marker.position);
29592 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29593 _this.fireEvent('mapClick', this, event);
29597 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29598 _this.fireEvent('mapRightClick', this, event);
29602 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29603 _this.fireEvent('markerClick', this, event);
29607 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29608 _this.fireEvent('markerRightClick', this, event);
29612 this.setPosition(this.gMapContext.location);
29614 this.fireEvent('initial', this, this.gMapContext.location);
29617 initOverlayView: function()
29621 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29625 _this.fireEvent('OverlayViewDraw', _this);
29630 _this.fireEvent('OverlayViewOnAdd', _this);
29633 onRemove: function()
29635 _this.fireEvent('OverlayViewOnRemove', _this);
29638 show: function(cpx)
29640 _this.fireEvent('OverlayViewShow', _this, cpx);
29645 _this.fireEvent('OverlayViewHide', _this);
29651 fromLatLngToContainerPixel: function(event)
29653 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29656 isApplied: function()
29658 return this.getGmapContext() == false ? false : true;
29661 getGmapContext: function()
29663 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29666 GMapContext: function()
29668 var position = new google.maps.LatLng(this.latitude, this.longitude);
29670 var _map = new google.maps.Map(this.el.dom, {
29673 mapTypeId: this.mapTypeId,
29674 mapTypeControl: this.mapTypeControl,
29675 disableDoubleClickZoom: this.disableDoubleClickZoom,
29676 scrollwheel: this.scrollwheel,
29677 streetViewControl: this.streetViewControl,
29678 locationName: this.locationName,
29679 draggable: this.draggable,
29680 enableAutocomplete: this.enableAutocomplete,
29681 enableReverseGeocode: this.enableReverseGeocode
29684 var _marker = new google.maps.Marker({
29685 position: position,
29687 title: this.markerTitle,
29688 draggable: this.draggable
29695 location: position,
29696 radius: this.radius,
29697 locationName: this.locationName,
29698 addressComponents: {
29699 formatted_address: null,
29700 addressLine1: null,
29701 addressLine2: null,
29703 streetNumber: null,
29707 stateOrProvince: null
29710 domContainer: this.el.dom,
29711 geodecoder: new google.maps.Geocoder()
29715 drawCircle: function(center, radius, options)
29717 if (this.gMapContext.circle != null) {
29718 this.gMapContext.circle.setMap(null);
29722 options = Roo.apply({}, options, {
29723 strokeColor: "#0000FF",
29724 strokeOpacity: .35,
29726 fillColor: "#0000FF",
29730 options.map = this.gMapContext.map;
29731 options.radius = radius;
29732 options.center = center;
29733 this.gMapContext.circle = new google.maps.Circle(options);
29734 return this.gMapContext.circle;
29740 setPosition: function(location)
29742 this.gMapContext.location = location;
29743 this.gMapContext.marker.setPosition(location);
29744 this.gMapContext.map.panTo(location);
29745 this.drawCircle(location, this.gMapContext.radius, {});
29749 if (this.gMapContext.settings.enableReverseGeocode) {
29750 this.gMapContext.geodecoder.geocode({
29751 latLng: this.gMapContext.location
29752 }, function(results, status) {
29754 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29755 _this.gMapContext.locationName = results[0].formatted_address;
29756 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29758 _this.fireEvent('positionchanged', this, location);
29765 this.fireEvent('positionchanged', this, location);
29770 google.maps.event.trigger(this.gMapContext.map, "resize");
29772 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29774 this.fireEvent('resize', this);
29777 setPositionByLatLng: function(latitude, longitude)
29779 this.setPosition(new google.maps.LatLng(latitude, longitude));
29782 getCurrentPosition: function()
29785 latitude: this.gMapContext.location.lat(),
29786 longitude: this.gMapContext.location.lng()
29790 getAddressName: function()
29792 return this.gMapContext.locationName;
29795 getAddressComponents: function()
29797 return this.gMapContext.addressComponents;
29800 address_component_from_google_geocode: function(address_components)
29804 for (var i = 0; i < address_components.length; i++) {
29805 var component = address_components[i];
29806 if (component.types.indexOf("postal_code") >= 0) {
29807 result.postalCode = component.short_name;
29808 } else if (component.types.indexOf("street_number") >= 0) {
29809 result.streetNumber = component.short_name;
29810 } else if (component.types.indexOf("route") >= 0) {
29811 result.streetName = component.short_name;
29812 } else if (component.types.indexOf("neighborhood") >= 0) {
29813 result.city = component.short_name;
29814 } else if (component.types.indexOf("locality") >= 0) {
29815 result.city = component.short_name;
29816 } else if (component.types.indexOf("sublocality") >= 0) {
29817 result.district = component.short_name;
29818 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29819 result.stateOrProvince = component.short_name;
29820 } else if (component.types.indexOf("country") >= 0) {
29821 result.country = component.short_name;
29825 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29826 result.addressLine2 = "";
29830 setZoomLevel: function(zoom)
29832 this.gMapContext.map.setZoom(zoom);
29845 this.fireEvent('show', this);
29856 this.fireEvent('hide', this);
29861 Roo.apply(Roo.bootstrap.LocationPicker, {
29863 OverlayView : function(map, options)
29865 options = options || {};
29872 * @class Roo.bootstrap.Alert
29873 * @extends Roo.bootstrap.Component
29874 * Bootstrap Alert class - shows an alert area box
29876 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29877 Enter a valid email address
29880 * @cfg {String} title The title of alert
29881 * @cfg {String} html The content of alert
29882 * @cfg {String} weight ( success | info | warning | danger )
29883 * @cfg {String} fa font-awesomeicon
29884 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29885 * @cfg {Boolean} close true to show a x closer
29889 * Create a new alert
29890 * @param {Object} config The config object
29894 Roo.bootstrap.Alert = function(config){
29895 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29899 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29905 faicon: false, // BC
29909 getAutoCreate : function()
29921 style : this.close ? '' : 'display:none'
29925 cls : 'roo-alert-icon'
29930 cls : 'roo-alert-title',
29935 cls : 'roo-alert-text',
29942 cfg.cn[0].cls += ' fa ' + this.faicon;
29945 cfg.cn[0].cls += ' fa ' + this.fa;
29949 cfg.cls += ' alert-' + this.weight;
29955 initEvents: function()
29957 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29958 this.titleEl = this.el.select('.roo-alert-title',true).first();
29959 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29960 if (this.seconds > 0) {
29961 this.hide.defer(this.seconds, this);
29965 setTitle : function(str)
29967 this.titleEl.dom.innerHTML = str;
29970 setText : function(str)
29972 this.titleEl.dom.innerHTML = str;
29975 setWeight : function(weight)
29978 this.el.removeClass('alert-' + this.weight);
29981 this.weight = weight;
29983 this.el.addClass('alert-' + this.weight);
29986 setIcon : function(icon)
29989 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29992 this.faicon = icon;
29994 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30015 * @class Roo.bootstrap.UploadCropbox
30016 * @extends Roo.bootstrap.Component
30017 * Bootstrap UploadCropbox class
30018 * @cfg {String} emptyText show when image has been loaded
30019 * @cfg {String} rotateNotify show when image too small to rotate
30020 * @cfg {Number} errorTimeout default 3000
30021 * @cfg {Number} minWidth default 300
30022 * @cfg {Number} minHeight default 300
30023 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30024 * @cfg {Boolean} isDocument (true|false) default false
30025 * @cfg {String} url action url
30026 * @cfg {String} paramName default 'imageUpload'
30027 * @cfg {String} method default POST
30028 * @cfg {Boolean} loadMask (true|false) default true
30029 * @cfg {Boolean} loadingText default 'Loading...'
30032 * Create a new UploadCropbox
30033 * @param {Object} config The config object
30036 Roo.bootstrap.UploadCropbox = function(config){
30037 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30041 * @event beforeselectfile
30042 * Fire before select file
30043 * @param {Roo.bootstrap.UploadCropbox} this
30045 "beforeselectfile" : true,
30048 * Fire after initEvent
30049 * @param {Roo.bootstrap.UploadCropbox} this
30054 * Fire after initEvent
30055 * @param {Roo.bootstrap.UploadCropbox} this
30056 * @param {String} data
30061 * Fire when preparing the file data
30062 * @param {Roo.bootstrap.UploadCropbox} this
30063 * @param {Object} file
30068 * Fire when get exception
30069 * @param {Roo.bootstrap.UploadCropbox} this
30070 * @param {XMLHttpRequest} xhr
30072 "exception" : true,
30074 * @event beforeloadcanvas
30075 * Fire before load the canvas
30076 * @param {Roo.bootstrap.UploadCropbox} this
30077 * @param {String} src
30079 "beforeloadcanvas" : true,
30082 * Fire when trash image
30083 * @param {Roo.bootstrap.UploadCropbox} this
30088 * Fire when download the image
30089 * @param {Roo.bootstrap.UploadCropbox} this
30093 * @event footerbuttonclick
30094 * Fire when footerbuttonclick
30095 * @param {Roo.bootstrap.UploadCropbox} this
30096 * @param {String} type
30098 "footerbuttonclick" : true,
30102 * @param {Roo.bootstrap.UploadCropbox} this
30107 * Fire when rotate the image
30108 * @param {Roo.bootstrap.UploadCropbox} this
30109 * @param {String} pos
30114 * Fire when inspect the file
30115 * @param {Roo.bootstrap.UploadCropbox} this
30116 * @param {Object} file
30121 * Fire when xhr upload the file
30122 * @param {Roo.bootstrap.UploadCropbox} this
30123 * @param {Object} data
30128 * Fire when arrange the file data
30129 * @param {Roo.bootstrap.UploadCropbox} this
30130 * @param {Object} formData
30135 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30138 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30140 emptyText : 'Click to upload image',
30141 rotateNotify : 'Image is too small to rotate',
30142 errorTimeout : 3000,
30156 cropType : 'image/jpeg',
30158 canvasLoaded : false,
30159 isDocument : false,
30161 paramName : 'imageUpload',
30163 loadingText : 'Loading...',
30166 getAutoCreate : function()
30170 cls : 'roo-upload-cropbox',
30174 cls : 'roo-upload-cropbox-selector',
30179 cls : 'roo-upload-cropbox-body',
30180 style : 'cursor:pointer',
30184 cls : 'roo-upload-cropbox-preview'
30188 cls : 'roo-upload-cropbox-thumb'
30192 cls : 'roo-upload-cropbox-empty-notify',
30193 html : this.emptyText
30197 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30198 html : this.rotateNotify
30204 cls : 'roo-upload-cropbox-footer',
30207 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30217 onRender : function(ct, position)
30219 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30221 if (this.buttons.length) {
30223 Roo.each(this.buttons, function(bb) {
30225 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30227 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30233 this.maskEl = this.el;
30237 initEvents : function()
30239 this.urlAPI = (window.createObjectURL && window) ||
30240 (window.URL && URL.revokeObjectURL && URL) ||
30241 (window.webkitURL && webkitURL);
30243 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30244 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30246 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30247 this.selectorEl.hide();
30249 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30250 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30252 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30253 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30254 this.thumbEl.hide();
30256 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30257 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30259 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30260 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30261 this.errorEl.hide();
30263 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30264 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30265 this.footerEl.hide();
30267 this.setThumbBoxSize();
30273 this.fireEvent('initial', this);
30280 window.addEventListener("resize", function() { _this.resize(); } );
30282 this.bodyEl.on('click', this.beforeSelectFile, this);
30285 this.bodyEl.on('touchstart', this.onTouchStart, this);
30286 this.bodyEl.on('touchmove', this.onTouchMove, this);
30287 this.bodyEl.on('touchend', this.onTouchEnd, this);
30291 this.bodyEl.on('mousedown', this.onMouseDown, this);
30292 this.bodyEl.on('mousemove', this.onMouseMove, this);
30293 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30294 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30295 Roo.get(document).on('mouseup', this.onMouseUp, this);
30298 this.selectorEl.on('change', this.onFileSelected, this);
30304 this.baseScale = 1;
30306 this.baseRotate = 1;
30307 this.dragable = false;
30308 this.pinching = false;
30311 this.cropData = false;
30312 this.notifyEl.dom.innerHTML = this.emptyText;
30314 this.selectorEl.dom.value = '';
30318 resize : function()
30320 if(this.fireEvent('resize', this) != false){
30321 this.setThumbBoxPosition();
30322 this.setCanvasPosition();
30326 onFooterButtonClick : function(e, el, o, type)
30329 case 'rotate-left' :
30330 this.onRotateLeft(e);
30332 case 'rotate-right' :
30333 this.onRotateRight(e);
30336 this.beforeSelectFile(e);
30351 this.fireEvent('footerbuttonclick', this, type);
30354 beforeSelectFile : function(e)
30356 e.preventDefault();
30358 if(this.fireEvent('beforeselectfile', this) != false){
30359 this.selectorEl.dom.click();
30363 onFileSelected : function(e)
30365 e.preventDefault();
30367 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30371 var file = this.selectorEl.dom.files[0];
30373 if(this.fireEvent('inspect', this, file) != false){
30374 this.prepare(file);
30379 trash : function(e)
30381 this.fireEvent('trash', this);
30384 download : function(e)
30386 this.fireEvent('download', this);
30389 loadCanvas : function(src)
30391 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30395 this.imageEl = document.createElement('img');
30399 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30401 this.imageEl.src = src;
30405 onLoadCanvas : function()
30407 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30408 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30410 this.bodyEl.un('click', this.beforeSelectFile, this);
30412 this.notifyEl.hide();
30413 this.thumbEl.show();
30414 this.footerEl.show();
30416 this.baseRotateLevel();
30418 if(this.isDocument){
30419 this.setThumbBoxSize();
30422 this.setThumbBoxPosition();
30424 this.baseScaleLevel();
30430 this.canvasLoaded = true;
30433 this.maskEl.unmask();
30438 setCanvasPosition : function()
30440 if(!this.canvasEl){
30444 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30445 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30447 this.previewEl.setLeft(pw);
30448 this.previewEl.setTop(ph);
30452 onMouseDown : function(e)
30456 this.dragable = true;
30457 this.pinching = false;
30459 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30460 this.dragable = false;
30464 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30465 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30469 onMouseMove : function(e)
30473 if(!this.canvasLoaded){
30477 if (!this.dragable){
30481 var minX = Math.ceil(this.thumbEl.getLeft(true));
30482 var minY = Math.ceil(this.thumbEl.getTop(true));
30484 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30485 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30487 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30488 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30490 x = x - this.mouseX;
30491 y = y - this.mouseY;
30493 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30494 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30496 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30497 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30499 this.previewEl.setLeft(bgX);
30500 this.previewEl.setTop(bgY);
30502 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30503 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30506 onMouseUp : function(e)
30510 this.dragable = false;
30513 onMouseWheel : function(e)
30517 this.startScale = this.scale;
30519 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30521 if(!this.zoomable()){
30522 this.scale = this.startScale;
30531 zoomable : function()
30533 var minScale = this.thumbEl.getWidth() / this.minWidth;
30535 if(this.minWidth < this.minHeight){
30536 minScale = this.thumbEl.getHeight() / this.minHeight;
30539 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30540 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30544 (this.rotate == 0 || this.rotate == 180) &&
30546 width > this.imageEl.OriginWidth ||
30547 height > this.imageEl.OriginHeight ||
30548 (width < this.minWidth && height < this.minHeight)
30556 (this.rotate == 90 || this.rotate == 270) &&
30558 width > this.imageEl.OriginWidth ||
30559 height > this.imageEl.OriginHeight ||
30560 (width < this.minHeight && height < this.minWidth)
30567 !this.isDocument &&
30568 (this.rotate == 0 || this.rotate == 180) &&
30570 width < this.minWidth ||
30571 width > this.imageEl.OriginWidth ||
30572 height < this.minHeight ||
30573 height > this.imageEl.OriginHeight
30580 !this.isDocument &&
30581 (this.rotate == 90 || this.rotate == 270) &&
30583 width < this.minHeight ||
30584 width > this.imageEl.OriginWidth ||
30585 height < this.minWidth ||
30586 height > this.imageEl.OriginHeight
30596 onRotateLeft : function(e)
30598 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30600 var minScale = this.thumbEl.getWidth() / this.minWidth;
30602 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30603 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30605 this.startScale = this.scale;
30607 while (this.getScaleLevel() < minScale){
30609 this.scale = this.scale + 1;
30611 if(!this.zoomable()){
30616 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30617 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30622 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30629 this.scale = this.startScale;
30631 this.onRotateFail();
30636 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30638 if(this.isDocument){
30639 this.setThumbBoxSize();
30640 this.setThumbBoxPosition();
30641 this.setCanvasPosition();
30646 this.fireEvent('rotate', this, 'left');
30650 onRotateRight : function(e)
30652 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30654 var minScale = this.thumbEl.getWidth() / this.minWidth;
30656 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30657 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30659 this.startScale = this.scale;
30661 while (this.getScaleLevel() < minScale){
30663 this.scale = this.scale + 1;
30665 if(!this.zoomable()){
30670 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30671 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30676 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30683 this.scale = this.startScale;
30685 this.onRotateFail();
30690 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30692 if(this.isDocument){
30693 this.setThumbBoxSize();
30694 this.setThumbBoxPosition();
30695 this.setCanvasPosition();
30700 this.fireEvent('rotate', this, 'right');
30703 onRotateFail : function()
30705 this.errorEl.show(true);
30709 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30714 this.previewEl.dom.innerHTML = '';
30716 var canvasEl = document.createElement("canvas");
30718 var contextEl = canvasEl.getContext("2d");
30720 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30721 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30722 var center = this.imageEl.OriginWidth / 2;
30724 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30725 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30726 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30727 center = this.imageEl.OriginHeight / 2;
30730 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30732 contextEl.translate(center, center);
30733 contextEl.rotate(this.rotate * Math.PI / 180);
30735 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30737 this.canvasEl = document.createElement("canvas");
30739 this.contextEl = this.canvasEl.getContext("2d");
30741 switch (this.rotate) {
30744 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30745 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30747 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30752 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30753 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30755 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30756 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);
30760 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30765 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30766 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30768 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30769 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);
30773 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);
30778 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30779 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30781 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30782 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30786 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);
30793 this.previewEl.appendChild(this.canvasEl);
30795 this.setCanvasPosition();
30800 if(!this.canvasLoaded){
30804 var imageCanvas = document.createElement("canvas");
30806 var imageContext = imageCanvas.getContext("2d");
30808 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30809 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30811 var center = imageCanvas.width / 2;
30813 imageContext.translate(center, center);
30815 imageContext.rotate(this.rotate * Math.PI / 180);
30817 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30819 var canvas = document.createElement("canvas");
30821 var context = canvas.getContext("2d");
30823 canvas.width = this.minWidth;
30824 canvas.height = this.minHeight;
30826 switch (this.rotate) {
30829 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30830 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30832 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30833 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30835 var targetWidth = this.minWidth - 2 * x;
30836 var targetHeight = this.minHeight - 2 * y;
30840 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30841 scale = targetWidth / width;
30844 if(x > 0 && y == 0){
30845 scale = targetHeight / height;
30848 if(x > 0 && y > 0){
30849 scale = targetWidth / width;
30851 if(width < height){
30852 scale = targetHeight / height;
30856 context.scale(scale, scale);
30858 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30859 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30861 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30862 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30864 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30869 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30870 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30872 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30873 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30875 var targetWidth = this.minWidth - 2 * x;
30876 var targetHeight = this.minHeight - 2 * y;
30880 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30881 scale = targetWidth / width;
30884 if(x > 0 && y == 0){
30885 scale = targetHeight / height;
30888 if(x > 0 && y > 0){
30889 scale = targetWidth / width;
30891 if(width < height){
30892 scale = targetHeight / height;
30896 context.scale(scale, scale);
30898 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30899 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30901 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30902 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30904 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30906 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30911 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30912 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30914 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30915 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30917 var targetWidth = this.minWidth - 2 * x;
30918 var targetHeight = this.minHeight - 2 * y;
30922 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30923 scale = targetWidth / width;
30926 if(x > 0 && y == 0){
30927 scale = targetHeight / height;
30930 if(x > 0 && y > 0){
30931 scale = targetWidth / width;
30933 if(width < height){
30934 scale = targetHeight / height;
30938 context.scale(scale, scale);
30940 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30941 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30943 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30944 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30946 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30947 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30949 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30954 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30955 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30957 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30958 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30960 var targetWidth = this.minWidth - 2 * x;
30961 var targetHeight = this.minHeight - 2 * y;
30965 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30966 scale = targetWidth / width;
30969 if(x > 0 && y == 0){
30970 scale = targetHeight / height;
30973 if(x > 0 && y > 0){
30974 scale = targetWidth / width;
30976 if(width < height){
30977 scale = targetHeight / height;
30981 context.scale(scale, scale);
30983 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30984 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30986 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30987 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30989 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30991 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30998 this.cropData = canvas.toDataURL(this.cropType);
31000 if(this.fireEvent('crop', this, this.cropData) !== false){
31001 this.process(this.file, this.cropData);
31008 setThumbBoxSize : function()
31012 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31013 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31014 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31016 this.minWidth = width;
31017 this.minHeight = height;
31019 if(this.rotate == 90 || this.rotate == 270){
31020 this.minWidth = height;
31021 this.minHeight = width;
31026 width = Math.ceil(this.minWidth * height / this.minHeight);
31028 if(this.minWidth > this.minHeight){
31030 height = Math.ceil(this.minHeight * width / this.minWidth);
31033 this.thumbEl.setStyle({
31034 width : width + 'px',
31035 height : height + 'px'
31042 setThumbBoxPosition : function()
31044 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31045 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31047 this.thumbEl.setLeft(x);
31048 this.thumbEl.setTop(y);
31052 baseRotateLevel : function()
31054 this.baseRotate = 1;
31057 typeof(this.exif) != 'undefined' &&
31058 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31059 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31061 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31064 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31068 baseScaleLevel : function()
31072 if(this.isDocument){
31074 if(this.baseRotate == 6 || this.baseRotate == 8){
31076 height = this.thumbEl.getHeight();
31077 this.baseScale = height / this.imageEl.OriginWidth;
31079 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31080 width = this.thumbEl.getWidth();
31081 this.baseScale = width / this.imageEl.OriginHeight;
31087 height = this.thumbEl.getHeight();
31088 this.baseScale = height / this.imageEl.OriginHeight;
31090 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31091 width = this.thumbEl.getWidth();
31092 this.baseScale = width / this.imageEl.OriginWidth;
31098 if(this.baseRotate == 6 || this.baseRotate == 8){
31100 width = this.thumbEl.getHeight();
31101 this.baseScale = width / this.imageEl.OriginHeight;
31103 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31104 height = this.thumbEl.getWidth();
31105 this.baseScale = height / this.imageEl.OriginHeight;
31108 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31109 height = this.thumbEl.getWidth();
31110 this.baseScale = height / this.imageEl.OriginHeight;
31112 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31113 width = this.thumbEl.getHeight();
31114 this.baseScale = width / this.imageEl.OriginWidth;
31121 width = this.thumbEl.getWidth();
31122 this.baseScale = width / this.imageEl.OriginWidth;
31124 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31125 height = this.thumbEl.getHeight();
31126 this.baseScale = height / this.imageEl.OriginHeight;
31129 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31131 height = this.thumbEl.getHeight();
31132 this.baseScale = height / this.imageEl.OriginHeight;
31134 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31135 width = this.thumbEl.getWidth();
31136 this.baseScale = width / this.imageEl.OriginWidth;
31144 getScaleLevel : function()
31146 return this.baseScale * Math.pow(1.1, this.scale);
31149 onTouchStart : function(e)
31151 if(!this.canvasLoaded){
31152 this.beforeSelectFile(e);
31156 var touches = e.browserEvent.touches;
31162 if(touches.length == 1){
31163 this.onMouseDown(e);
31167 if(touches.length != 2){
31173 for(var i = 0, finger; finger = touches[i]; i++){
31174 coords.push(finger.pageX, finger.pageY);
31177 var x = Math.pow(coords[0] - coords[2], 2);
31178 var y = Math.pow(coords[1] - coords[3], 2);
31180 this.startDistance = Math.sqrt(x + y);
31182 this.startScale = this.scale;
31184 this.pinching = true;
31185 this.dragable = false;
31189 onTouchMove : function(e)
31191 if(!this.pinching && !this.dragable){
31195 var touches = e.browserEvent.touches;
31202 this.onMouseMove(e);
31208 for(var i = 0, finger; finger = touches[i]; i++){
31209 coords.push(finger.pageX, finger.pageY);
31212 var x = Math.pow(coords[0] - coords[2], 2);
31213 var y = Math.pow(coords[1] - coords[3], 2);
31215 this.endDistance = Math.sqrt(x + y);
31217 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31219 if(!this.zoomable()){
31220 this.scale = this.startScale;
31228 onTouchEnd : function(e)
31230 this.pinching = false;
31231 this.dragable = false;
31235 process : function(file, crop)
31238 this.maskEl.mask(this.loadingText);
31241 this.xhr = new XMLHttpRequest();
31243 file.xhr = this.xhr;
31245 this.xhr.open(this.method, this.url, true);
31248 "Accept": "application/json",
31249 "Cache-Control": "no-cache",
31250 "X-Requested-With": "XMLHttpRequest"
31253 for (var headerName in headers) {
31254 var headerValue = headers[headerName];
31256 this.xhr.setRequestHeader(headerName, headerValue);
31262 this.xhr.onload = function()
31264 _this.xhrOnLoad(_this.xhr);
31267 this.xhr.onerror = function()
31269 _this.xhrOnError(_this.xhr);
31272 var formData = new FormData();
31274 formData.append('returnHTML', 'NO');
31277 formData.append('crop', crop);
31280 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31281 formData.append(this.paramName, file, file.name);
31284 if(typeof(file.filename) != 'undefined'){
31285 formData.append('filename', file.filename);
31288 if(typeof(file.mimetype) != 'undefined'){
31289 formData.append('mimetype', file.mimetype);
31292 if(this.fireEvent('arrange', this, formData) != false){
31293 this.xhr.send(formData);
31297 xhrOnLoad : function(xhr)
31300 this.maskEl.unmask();
31303 if (xhr.readyState !== 4) {
31304 this.fireEvent('exception', this, xhr);
31308 var response = Roo.decode(xhr.responseText);
31310 if(!response.success){
31311 this.fireEvent('exception', this, xhr);
31315 var response = Roo.decode(xhr.responseText);
31317 this.fireEvent('upload', this, response);
31321 xhrOnError : function()
31324 this.maskEl.unmask();
31327 Roo.log('xhr on error');
31329 var response = Roo.decode(xhr.responseText);
31335 prepare : function(file)
31338 this.maskEl.mask(this.loadingText);
31344 if(typeof(file) === 'string'){
31345 this.loadCanvas(file);
31349 if(!file || !this.urlAPI){
31354 this.cropType = file.type;
31358 if(this.fireEvent('prepare', this, this.file) != false){
31360 var reader = new FileReader();
31362 reader.onload = function (e) {
31363 if (e.target.error) {
31364 Roo.log(e.target.error);
31368 var buffer = e.target.result,
31369 dataView = new DataView(buffer),
31371 maxOffset = dataView.byteLength - 4,
31375 if (dataView.getUint16(0) === 0xffd8) {
31376 while (offset < maxOffset) {
31377 markerBytes = dataView.getUint16(offset);
31379 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31380 markerLength = dataView.getUint16(offset + 2) + 2;
31381 if (offset + markerLength > dataView.byteLength) {
31382 Roo.log('Invalid meta data: Invalid segment size.');
31386 if(markerBytes == 0xffe1){
31387 _this.parseExifData(
31394 offset += markerLength;
31404 var url = _this.urlAPI.createObjectURL(_this.file);
31406 _this.loadCanvas(url);
31411 reader.readAsArrayBuffer(this.file);
31417 parseExifData : function(dataView, offset, length)
31419 var tiffOffset = offset + 10,
31423 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31424 // No Exif data, might be XMP data instead
31428 // Check for the ASCII code for "Exif" (0x45786966):
31429 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31430 // No Exif data, might be XMP data instead
31433 if (tiffOffset + 8 > dataView.byteLength) {
31434 Roo.log('Invalid Exif data: Invalid segment size.');
31437 // Check for the two null bytes:
31438 if (dataView.getUint16(offset + 8) !== 0x0000) {
31439 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31442 // Check the byte alignment:
31443 switch (dataView.getUint16(tiffOffset)) {
31445 littleEndian = true;
31448 littleEndian = false;
31451 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31454 // Check for the TIFF tag marker (0x002A):
31455 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31456 Roo.log('Invalid Exif data: Missing TIFF marker.');
31459 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31460 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31462 this.parseExifTags(
31465 tiffOffset + dirOffset,
31470 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31475 if (dirOffset + 6 > dataView.byteLength) {
31476 Roo.log('Invalid Exif data: Invalid directory offset.');
31479 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31480 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31481 if (dirEndOffset + 4 > dataView.byteLength) {
31482 Roo.log('Invalid Exif data: Invalid directory size.');
31485 for (i = 0; i < tagsNumber; i += 1) {
31489 dirOffset + 2 + 12 * i, // tag offset
31493 // Return the offset to the next directory:
31494 return dataView.getUint32(dirEndOffset, littleEndian);
31497 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31499 var tag = dataView.getUint16(offset, littleEndian);
31501 this.exif[tag] = this.getExifValue(
31505 dataView.getUint16(offset + 2, littleEndian), // tag type
31506 dataView.getUint32(offset + 4, littleEndian), // tag length
31511 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31513 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31522 Roo.log('Invalid Exif data: Invalid tag type.');
31526 tagSize = tagType.size * length;
31527 // Determine if the value is contained in the dataOffset bytes,
31528 // or if the value at the dataOffset is a pointer to the actual data:
31529 dataOffset = tagSize > 4 ?
31530 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31531 if (dataOffset + tagSize > dataView.byteLength) {
31532 Roo.log('Invalid Exif data: Invalid data offset.');
31535 if (length === 1) {
31536 return tagType.getValue(dataView, dataOffset, littleEndian);
31539 for (i = 0; i < length; i += 1) {
31540 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31543 if (tagType.ascii) {
31545 // Concatenate the chars:
31546 for (i = 0; i < values.length; i += 1) {
31548 // Ignore the terminating NULL byte(s):
31549 if (c === '\u0000') {
31561 Roo.apply(Roo.bootstrap.UploadCropbox, {
31563 'Orientation': 0x0112
31567 1: 0, //'top-left',
31569 3: 180, //'bottom-right',
31570 // 4: 'bottom-left',
31572 6: 90, //'right-top',
31573 // 7: 'right-bottom',
31574 8: 270 //'left-bottom'
31578 // byte, 8-bit unsigned int:
31580 getValue: function (dataView, dataOffset) {
31581 return dataView.getUint8(dataOffset);
31585 // ascii, 8-bit byte:
31587 getValue: function (dataView, dataOffset) {
31588 return String.fromCharCode(dataView.getUint8(dataOffset));
31593 // short, 16 bit int:
31595 getValue: function (dataView, dataOffset, littleEndian) {
31596 return dataView.getUint16(dataOffset, littleEndian);
31600 // long, 32 bit int:
31602 getValue: function (dataView, dataOffset, littleEndian) {
31603 return dataView.getUint32(dataOffset, littleEndian);
31607 // rational = two long values, first is numerator, second is denominator:
31609 getValue: function (dataView, dataOffset, littleEndian) {
31610 return dataView.getUint32(dataOffset, littleEndian) /
31611 dataView.getUint32(dataOffset + 4, littleEndian);
31615 // slong, 32 bit signed int:
31617 getValue: function (dataView, dataOffset, littleEndian) {
31618 return dataView.getInt32(dataOffset, littleEndian);
31622 // srational, two slongs, first is numerator, second is denominator:
31624 getValue: function (dataView, dataOffset, littleEndian) {
31625 return dataView.getInt32(dataOffset, littleEndian) /
31626 dataView.getInt32(dataOffset + 4, littleEndian);
31636 cls : 'btn-group roo-upload-cropbox-rotate-left',
31637 action : 'rotate-left',
31641 cls : 'btn btn-default',
31642 html : '<i class="fa fa-undo"></i>'
31648 cls : 'btn-group roo-upload-cropbox-picture',
31649 action : 'picture',
31653 cls : 'btn btn-default',
31654 html : '<i class="fa fa-picture-o"></i>'
31660 cls : 'btn-group roo-upload-cropbox-rotate-right',
31661 action : 'rotate-right',
31665 cls : 'btn btn-default',
31666 html : '<i class="fa fa-repeat"></i>'
31674 cls : 'btn-group roo-upload-cropbox-rotate-left',
31675 action : 'rotate-left',
31679 cls : 'btn btn-default',
31680 html : '<i class="fa fa-undo"></i>'
31686 cls : 'btn-group roo-upload-cropbox-download',
31687 action : 'download',
31691 cls : 'btn btn-default',
31692 html : '<i class="fa fa-download"></i>'
31698 cls : 'btn-group roo-upload-cropbox-crop',
31703 cls : 'btn btn-default',
31704 html : '<i class="fa fa-crop"></i>'
31710 cls : 'btn-group roo-upload-cropbox-trash',
31715 cls : 'btn btn-default',
31716 html : '<i class="fa fa-trash"></i>'
31722 cls : 'btn-group roo-upload-cropbox-rotate-right',
31723 action : 'rotate-right',
31727 cls : 'btn btn-default',
31728 html : '<i class="fa fa-repeat"></i>'
31736 cls : 'btn-group roo-upload-cropbox-rotate-left',
31737 action : 'rotate-left',
31741 cls : 'btn btn-default',
31742 html : '<i class="fa fa-undo"></i>'
31748 cls : 'btn-group roo-upload-cropbox-rotate-right',
31749 action : 'rotate-right',
31753 cls : 'btn btn-default',
31754 html : '<i class="fa fa-repeat"></i>'
31767 * @class Roo.bootstrap.DocumentManager
31768 * @extends Roo.bootstrap.Component
31769 * Bootstrap DocumentManager class
31770 * @cfg {String} paramName default 'imageUpload'
31771 * @cfg {String} toolTipName default 'filename'
31772 * @cfg {String} method default POST
31773 * @cfg {String} url action url
31774 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31775 * @cfg {Boolean} multiple multiple upload default true
31776 * @cfg {Number} thumbSize default 300
31777 * @cfg {String} fieldLabel
31778 * @cfg {Number} labelWidth default 4
31779 * @cfg {String} labelAlign (left|top) default left
31780 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31781 * @cfg {Number} labellg set the width of label (1-12)
31782 * @cfg {Number} labelmd set the width of label (1-12)
31783 * @cfg {Number} labelsm set the width of label (1-12)
31784 * @cfg {Number} labelxs set the width of label (1-12)
31787 * Create a new DocumentManager
31788 * @param {Object} config The config object
31791 Roo.bootstrap.DocumentManager = function(config){
31792 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31795 this.delegates = [];
31800 * Fire when initial the DocumentManager
31801 * @param {Roo.bootstrap.DocumentManager} this
31806 * inspect selected file
31807 * @param {Roo.bootstrap.DocumentManager} this
31808 * @param {File} file
31813 * Fire when xhr load exception
31814 * @param {Roo.bootstrap.DocumentManager} this
31815 * @param {XMLHttpRequest} xhr
31817 "exception" : true,
31819 * @event afterupload
31820 * Fire when xhr load exception
31821 * @param {Roo.bootstrap.DocumentManager} this
31822 * @param {XMLHttpRequest} xhr
31824 "afterupload" : true,
31827 * prepare the form data
31828 * @param {Roo.bootstrap.DocumentManager} this
31829 * @param {Object} formData
31834 * Fire when remove the file
31835 * @param {Roo.bootstrap.DocumentManager} this
31836 * @param {Object} file
31841 * Fire after refresh the file
31842 * @param {Roo.bootstrap.DocumentManager} this
31847 * Fire after click the image
31848 * @param {Roo.bootstrap.DocumentManager} this
31849 * @param {Object} file
31854 * Fire when upload a image and editable set to true
31855 * @param {Roo.bootstrap.DocumentManager} this
31856 * @param {Object} file
31860 * @event beforeselectfile
31861 * Fire before select file
31862 * @param {Roo.bootstrap.DocumentManager} this
31864 "beforeselectfile" : true,
31867 * Fire before process file
31868 * @param {Roo.bootstrap.DocumentManager} this
31869 * @param {Object} file
31873 * @event previewrendered
31874 * Fire when preview rendered
31875 * @param {Roo.bootstrap.DocumentManager} this
31876 * @param {Object} file
31878 "previewrendered" : true,
31881 "previewResize" : true
31886 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31895 paramName : 'imageUpload',
31896 toolTipName : 'filename',
31899 labelAlign : 'left',
31909 getAutoCreate : function()
31911 var managerWidget = {
31913 cls : 'roo-document-manager',
31917 cls : 'roo-document-manager-selector',
31922 cls : 'roo-document-manager-uploader',
31926 cls : 'roo-document-manager-upload-btn',
31927 html : '<i class="fa fa-plus"></i>'
31938 cls : 'column col-md-12',
31943 if(this.fieldLabel.length){
31948 cls : 'column col-md-12',
31949 html : this.fieldLabel
31953 cls : 'column col-md-12',
31958 if(this.labelAlign == 'left'){
31963 html : this.fieldLabel
31972 if(this.labelWidth > 12){
31973 content[0].style = "width: " + this.labelWidth + 'px';
31976 if(this.labelWidth < 13 && this.labelmd == 0){
31977 this.labelmd = this.labelWidth;
31980 if(this.labellg > 0){
31981 content[0].cls += ' col-lg-' + this.labellg;
31982 content[1].cls += ' col-lg-' + (12 - this.labellg);
31985 if(this.labelmd > 0){
31986 content[0].cls += ' col-md-' + this.labelmd;
31987 content[1].cls += ' col-md-' + (12 - this.labelmd);
31990 if(this.labelsm > 0){
31991 content[0].cls += ' col-sm-' + this.labelsm;
31992 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31995 if(this.labelxs > 0){
31996 content[0].cls += ' col-xs-' + this.labelxs;
31997 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32005 cls : 'row clearfix',
32013 initEvents : function()
32015 this.managerEl = this.el.select('.roo-document-manager', true).first();
32016 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32018 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32019 this.selectorEl.hide();
32022 this.selectorEl.attr('multiple', 'multiple');
32025 this.selectorEl.on('change', this.onFileSelected, this);
32027 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32028 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32030 this.uploader.on('click', this.onUploaderClick, this);
32032 this.renderProgressDialog();
32036 window.addEventListener("resize", function() { _this.refresh(); } );
32038 this.fireEvent('initial', this);
32041 renderProgressDialog : function()
32045 this.progressDialog = new Roo.bootstrap.Modal({
32046 cls : 'roo-document-manager-progress-dialog',
32047 allow_close : false,
32058 btnclick : function() {
32059 _this.uploadCancel();
32065 this.progressDialog.render(Roo.get(document.body));
32067 this.progress = new Roo.bootstrap.Progress({
32068 cls : 'roo-document-manager-progress',
32073 this.progress.render(this.progressDialog.getChildContainer());
32075 this.progressBar = new Roo.bootstrap.ProgressBar({
32076 cls : 'roo-document-manager-progress-bar',
32079 aria_valuemax : 12,
32083 this.progressBar.render(this.progress.getChildContainer());
32086 onUploaderClick : function(e)
32088 e.preventDefault();
32090 if(this.fireEvent('beforeselectfile', this) != false){
32091 this.selectorEl.dom.click();
32096 onFileSelected : function(e)
32098 e.preventDefault();
32100 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32104 Roo.each(this.selectorEl.dom.files, function(file){
32105 if(this.fireEvent('inspect', this, file) != false){
32106 this.files.push(file);
32116 this.selectorEl.dom.value = '';
32118 if(!this.files || !this.files.length){
32122 if(this.boxes > 0 && this.files.length > this.boxes){
32123 this.files = this.files.slice(0, this.boxes);
32126 this.uploader.show();
32128 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32129 this.uploader.hide();
32138 Roo.each(this.files, function(file){
32140 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32141 var f = this.renderPreview(file);
32146 if(file.type.indexOf('image') != -1){
32147 this.delegates.push(
32149 _this.process(file);
32150 }).createDelegate(this)
32158 _this.process(file);
32159 }).createDelegate(this)
32164 this.files = files;
32166 this.delegates = this.delegates.concat(docs);
32168 if(!this.delegates.length){
32173 this.progressBar.aria_valuemax = this.delegates.length;
32180 arrange : function()
32182 if(!this.delegates.length){
32183 this.progressDialog.hide();
32188 var delegate = this.delegates.shift();
32190 this.progressDialog.show();
32192 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32194 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32199 refresh : function()
32201 this.uploader.show();
32203 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32204 this.uploader.hide();
32207 Roo.isTouch ? this.closable(false) : this.closable(true);
32209 this.fireEvent('refresh', this);
32212 onRemove : function(e, el, o)
32214 e.preventDefault();
32216 this.fireEvent('remove', this, o);
32220 remove : function(o)
32224 Roo.each(this.files, function(file){
32225 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32234 this.files = files;
32241 Roo.each(this.files, function(file){
32246 file.target.remove();
32255 onClick : function(e, el, o)
32257 e.preventDefault();
32259 this.fireEvent('click', this, o);
32263 closable : function(closable)
32265 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32267 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32279 xhrOnLoad : function(xhr)
32281 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32285 if (xhr.readyState !== 4) {
32287 this.fireEvent('exception', this, xhr);
32291 var response = Roo.decode(xhr.responseText);
32293 if(!response.success){
32295 this.fireEvent('exception', this, xhr);
32299 var file = this.renderPreview(response.data);
32301 this.files.push(file);
32305 this.fireEvent('afterupload', this, xhr);
32309 xhrOnError : function(xhr)
32311 Roo.log('xhr on error');
32313 var response = Roo.decode(xhr.responseText);
32320 process : function(file)
32322 if(this.fireEvent('process', this, file) !== false){
32323 if(this.editable && file.type.indexOf('image') != -1){
32324 this.fireEvent('edit', this, file);
32328 this.uploadStart(file, false);
32335 uploadStart : function(file, crop)
32337 this.xhr = new XMLHttpRequest();
32339 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32344 file.xhr = this.xhr;
32346 this.managerEl.createChild({
32348 cls : 'roo-document-manager-loading',
32352 tooltip : file.name,
32353 cls : 'roo-document-manager-thumb',
32354 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32360 this.xhr.open(this.method, this.url, true);
32363 "Accept": "application/json",
32364 "Cache-Control": "no-cache",
32365 "X-Requested-With": "XMLHttpRequest"
32368 for (var headerName in headers) {
32369 var headerValue = headers[headerName];
32371 this.xhr.setRequestHeader(headerName, headerValue);
32377 this.xhr.onload = function()
32379 _this.xhrOnLoad(_this.xhr);
32382 this.xhr.onerror = function()
32384 _this.xhrOnError(_this.xhr);
32387 var formData = new FormData();
32389 formData.append('returnHTML', 'NO');
32392 formData.append('crop', crop);
32395 formData.append(this.paramName, file, file.name);
32402 if(this.fireEvent('prepare', this, formData, options) != false){
32404 if(options.manually){
32408 this.xhr.send(formData);
32412 this.uploadCancel();
32415 uploadCancel : function()
32421 this.delegates = [];
32423 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32430 renderPreview : function(file)
32432 if(typeof(file.target) != 'undefined' && file.target){
32436 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32438 var previewEl = this.managerEl.createChild({
32440 cls : 'roo-document-manager-preview',
32444 tooltip : file[this.toolTipName],
32445 cls : 'roo-document-manager-thumb',
32446 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32451 html : '<i class="fa fa-times-circle"></i>'
32456 var close = previewEl.select('button.close', true).first();
32458 close.on('click', this.onRemove, this, file);
32460 file.target = previewEl;
32462 var image = previewEl.select('img', true).first();
32466 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32468 image.on('click', this.onClick, this, file);
32470 this.fireEvent('previewrendered', this, file);
32476 onPreviewLoad : function(file, image)
32478 if(typeof(file.target) == 'undefined' || !file.target){
32482 var width = image.dom.naturalWidth || image.dom.width;
32483 var height = image.dom.naturalHeight || image.dom.height;
32485 if(!this.previewResize) {
32489 if(width > height){
32490 file.target.addClass('wide');
32494 file.target.addClass('tall');
32499 uploadFromSource : function(file, crop)
32501 this.xhr = new XMLHttpRequest();
32503 this.managerEl.createChild({
32505 cls : 'roo-document-manager-loading',
32509 tooltip : file.name,
32510 cls : 'roo-document-manager-thumb',
32511 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32517 this.xhr.open(this.method, this.url, true);
32520 "Accept": "application/json",
32521 "Cache-Control": "no-cache",
32522 "X-Requested-With": "XMLHttpRequest"
32525 for (var headerName in headers) {
32526 var headerValue = headers[headerName];
32528 this.xhr.setRequestHeader(headerName, headerValue);
32534 this.xhr.onload = function()
32536 _this.xhrOnLoad(_this.xhr);
32539 this.xhr.onerror = function()
32541 _this.xhrOnError(_this.xhr);
32544 var formData = new FormData();
32546 formData.append('returnHTML', 'NO');
32548 formData.append('crop', crop);
32550 if(typeof(file.filename) != 'undefined'){
32551 formData.append('filename', file.filename);
32554 if(typeof(file.mimetype) != 'undefined'){
32555 formData.append('mimetype', file.mimetype);
32560 if(this.fireEvent('prepare', this, formData) != false){
32561 this.xhr.send(formData);
32571 * @class Roo.bootstrap.DocumentViewer
32572 * @extends Roo.bootstrap.Component
32573 * Bootstrap DocumentViewer class
32574 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32575 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32578 * Create a new DocumentViewer
32579 * @param {Object} config The config object
32582 Roo.bootstrap.DocumentViewer = function(config){
32583 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32588 * Fire after initEvent
32589 * @param {Roo.bootstrap.DocumentViewer} this
32595 * @param {Roo.bootstrap.DocumentViewer} this
32600 * Fire after download button
32601 * @param {Roo.bootstrap.DocumentViewer} this
32606 * Fire after trash button
32607 * @param {Roo.bootstrap.DocumentViewer} this
32614 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32616 showDownload : true,
32620 getAutoCreate : function()
32624 cls : 'roo-document-viewer',
32628 cls : 'roo-document-viewer-body',
32632 cls : 'roo-document-viewer-thumb',
32636 cls : 'roo-document-viewer-image'
32644 cls : 'roo-document-viewer-footer',
32647 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32651 cls : 'btn-group roo-document-viewer-download',
32655 cls : 'btn btn-default',
32656 html : '<i class="fa fa-download"></i>'
32662 cls : 'btn-group roo-document-viewer-trash',
32666 cls : 'btn btn-default',
32667 html : '<i class="fa fa-trash"></i>'
32680 initEvents : function()
32682 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32683 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32685 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32686 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32688 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32689 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32691 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32692 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32694 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32695 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32697 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32698 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32700 this.bodyEl.on('click', this.onClick, this);
32701 this.downloadBtn.on('click', this.onDownload, this);
32702 this.trashBtn.on('click', this.onTrash, this);
32704 this.downloadBtn.hide();
32705 this.trashBtn.hide();
32707 if(this.showDownload){
32708 this.downloadBtn.show();
32711 if(this.showTrash){
32712 this.trashBtn.show();
32715 if(!this.showDownload && !this.showTrash) {
32716 this.footerEl.hide();
32721 initial : function()
32723 this.fireEvent('initial', this);
32727 onClick : function(e)
32729 e.preventDefault();
32731 this.fireEvent('click', this);
32734 onDownload : function(e)
32736 e.preventDefault();
32738 this.fireEvent('download', this);
32741 onTrash : function(e)
32743 e.preventDefault();
32745 this.fireEvent('trash', this);
32757 * @class Roo.bootstrap.NavProgressBar
32758 * @extends Roo.bootstrap.Component
32759 * Bootstrap NavProgressBar class
32762 * Create a new nav progress bar
32763 * @param {Object} config The config object
32766 Roo.bootstrap.NavProgressBar = function(config){
32767 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32769 this.bullets = this.bullets || [];
32771 // Roo.bootstrap.NavProgressBar.register(this);
32775 * Fires when the active item changes
32776 * @param {Roo.bootstrap.NavProgressBar} this
32777 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32778 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32785 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32790 getAutoCreate : function()
32792 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32796 cls : 'roo-navigation-bar-group',
32800 cls : 'roo-navigation-top-bar'
32804 cls : 'roo-navigation-bullets-bar',
32808 cls : 'roo-navigation-bar'
32815 cls : 'roo-navigation-bottom-bar'
32825 initEvents: function()
32830 onRender : function(ct, position)
32832 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32834 if(this.bullets.length){
32835 Roo.each(this.bullets, function(b){
32844 addItem : function(cfg)
32846 var item = new Roo.bootstrap.NavProgressItem(cfg);
32848 item.parentId = this.id;
32849 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32852 var top = new Roo.bootstrap.Element({
32854 cls : 'roo-navigation-bar-text'
32857 var bottom = new Roo.bootstrap.Element({
32859 cls : 'roo-navigation-bar-text'
32862 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32863 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32865 var topText = new Roo.bootstrap.Element({
32867 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32870 var bottomText = new Roo.bootstrap.Element({
32872 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32875 topText.onRender(top.el, null);
32876 bottomText.onRender(bottom.el, null);
32879 item.bottomEl = bottom;
32882 this.barItems.push(item);
32887 getActive : function()
32889 var active = false;
32891 Roo.each(this.barItems, function(v){
32893 if (!v.isActive()) {
32905 setActiveItem : function(item)
32909 Roo.each(this.barItems, function(v){
32910 if (v.rid == item.rid) {
32914 if (v.isActive()) {
32915 v.setActive(false);
32920 item.setActive(true);
32922 this.fireEvent('changed', this, item, prev);
32925 getBarItem: function(rid)
32929 Roo.each(this.barItems, function(e) {
32930 if (e.rid != rid) {
32941 indexOfItem : function(item)
32945 Roo.each(this.barItems, function(v, i){
32947 if (v.rid != item.rid) {
32958 setActiveNext : function()
32960 var i = this.indexOfItem(this.getActive());
32962 if (i > this.barItems.length) {
32966 this.setActiveItem(this.barItems[i+1]);
32969 setActivePrev : function()
32971 var i = this.indexOfItem(this.getActive());
32977 this.setActiveItem(this.barItems[i-1]);
32980 format : function()
32982 if(!this.barItems.length){
32986 var width = 100 / this.barItems.length;
32988 Roo.each(this.barItems, function(i){
32989 i.el.setStyle('width', width + '%');
32990 i.topEl.el.setStyle('width', width + '%');
32991 i.bottomEl.el.setStyle('width', width + '%');
33000 * Nav Progress Item
33005 * @class Roo.bootstrap.NavProgressItem
33006 * @extends Roo.bootstrap.Component
33007 * Bootstrap NavProgressItem class
33008 * @cfg {String} rid the reference id
33009 * @cfg {Boolean} active (true|false) Is item active default false
33010 * @cfg {Boolean} disabled (true|false) Is item active default false
33011 * @cfg {String} html
33012 * @cfg {String} position (top|bottom) text position default bottom
33013 * @cfg {String} icon show icon instead of number
33016 * Create a new NavProgressItem
33017 * @param {Object} config The config object
33019 Roo.bootstrap.NavProgressItem = function(config){
33020 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33025 * The raw click event for the entire grid.
33026 * @param {Roo.bootstrap.NavProgressItem} this
33027 * @param {Roo.EventObject} e
33034 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33040 position : 'bottom',
33043 getAutoCreate : function()
33045 var iconCls = 'roo-navigation-bar-item-icon';
33047 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33051 cls: 'roo-navigation-bar-item',
33061 cfg.cls += ' active';
33064 cfg.cls += ' disabled';
33070 disable : function()
33072 this.setDisabled(true);
33075 enable : function()
33077 this.setDisabled(false);
33080 initEvents: function()
33082 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33084 this.iconEl.on('click', this.onClick, this);
33087 onClick : function(e)
33089 e.preventDefault();
33095 if(this.fireEvent('click', this, e) === false){
33099 this.parent().setActiveItem(this);
33102 isActive: function ()
33104 return this.active;
33107 setActive : function(state)
33109 if(this.active == state){
33113 this.active = state;
33116 this.el.addClass('active');
33120 this.el.removeClass('active');
33125 setDisabled : function(state)
33127 if(this.disabled == state){
33131 this.disabled = state;
33134 this.el.addClass('disabled');
33138 this.el.removeClass('disabled');
33141 tooltipEl : function()
33143 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33156 * @class Roo.bootstrap.FieldLabel
33157 * @extends Roo.bootstrap.Component
33158 * Bootstrap FieldLabel class
33159 * @cfg {String} html contents of the element
33160 * @cfg {String} tag tag of the element default label
33161 * @cfg {String} cls class of the element
33162 * @cfg {String} target label target
33163 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33164 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33165 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33166 * @cfg {String} iconTooltip default "This field is required"
33167 * @cfg {String} indicatorpos (left|right) default left
33170 * Create a new FieldLabel
33171 * @param {Object} config The config object
33174 Roo.bootstrap.FieldLabel = function(config){
33175 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33180 * Fires after the field has been marked as invalid.
33181 * @param {Roo.form.FieldLabel} this
33182 * @param {String} msg The validation message
33187 * Fires after the field has been validated with no errors.
33188 * @param {Roo.form.FieldLabel} this
33194 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33201 invalidClass : 'has-warning',
33202 validClass : 'has-success',
33203 iconTooltip : 'This field is required',
33204 indicatorpos : 'left',
33206 getAutoCreate : function(){
33209 if (!this.allowBlank) {
33215 cls : 'roo-bootstrap-field-label ' + this.cls,
33220 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33221 tooltip : this.iconTooltip
33230 if(this.indicatorpos == 'right'){
33233 cls : 'roo-bootstrap-field-label ' + this.cls,
33242 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33243 tooltip : this.iconTooltip
33252 initEvents: function()
33254 Roo.bootstrap.Element.superclass.initEvents.call(this);
33256 this.indicator = this.indicatorEl();
33258 if(this.indicator){
33259 this.indicator.removeClass('visible');
33260 this.indicator.addClass('invisible');
33263 Roo.bootstrap.FieldLabel.register(this);
33266 indicatorEl : function()
33268 var indicator = this.el.select('i.roo-required-indicator',true).first();
33279 * Mark this field as valid
33281 markValid : function()
33283 if(this.indicator){
33284 this.indicator.removeClass('visible');
33285 this.indicator.addClass('invisible');
33287 if (Roo.bootstrap.version == 3) {
33288 this.el.removeClass(this.invalidClass);
33289 this.el.addClass(this.validClass);
33291 this.el.removeClass('is-invalid');
33292 this.el.addClass('is-valid');
33296 this.fireEvent('valid', this);
33300 * Mark this field as invalid
33301 * @param {String} msg The validation message
33303 markInvalid : function(msg)
33305 if(this.indicator){
33306 this.indicator.removeClass('invisible');
33307 this.indicator.addClass('visible');
33309 if (Roo.bootstrap.version == 3) {
33310 this.el.removeClass(this.validClass);
33311 this.el.addClass(this.invalidClass);
33313 this.el.removeClass('is-valid');
33314 this.el.addClass('is-invalid');
33318 this.fireEvent('invalid', this, msg);
33324 Roo.apply(Roo.bootstrap.FieldLabel, {
33329 * register a FieldLabel Group
33330 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33332 register : function(label)
33334 if(this.groups.hasOwnProperty(label.target)){
33338 this.groups[label.target] = label;
33342 * fetch a FieldLabel Group based on the target
33343 * @param {string} target
33344 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33346 get: function(target) {
33347 if (typeof(this.groups[target]) == 'undefined') {
33351 return this.groups[target] ;
33360 * page DateSplitField.
33366 * @class Roo.bootstrap.DateSplitField
33367 * @extends Roo.bootstrap.Component
33368 * Bootstrap DateSplitField class
33369 * @cfg {string} fieldLabel - the label associated
33370 * @cfg {Number} labelWidth set the width of label (0-12)
33371 * @cfg {String} labelAlign (top|left)
33372 * @cfg {Boolean} dayAllowBlank (true|false) default false
33373 * @cfg {Boolean} monthAllowBlank (true|false) default false
33374 * @cfg {Boolean} yearAllowBlank (true|false) default false
33375 * @cfg {string} dayPlaceholder
33376 * @cfg {string} monthPlaceholder
33377 * @cfg {string} yearPlaceholder
33378 * @cfg {string} dayFormat default 'd'
33379 * @cfg {string} monthFormat default 'm'
33380 * @cfg {string} yearFormat default 'Y'
33381 * @cfg {Number} labellg set the width of label (1-12)
33382 * @cfg {Number} labelmd set the width of label (1-12)
33383 * @cfg {Number} labelsm set the width of label (1-12)
33384 * @cfg {Number} labelxs set the width of label (1-12)
33388 * Create a new DateSplitField
33389 * @param {Object} config The config object
33392 Roo.bootstrap.DateSplitField = function(config){
33393 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33399 * getting the data of years
33400 * @param {Roo.bootstrap.DateSplitField} this
33401 * @param {Object} years
33406 * getting the data of days
33407 * @param {Roo.bootstrap.DateSplitField} this
33408 * @param {Object} days
33413 * Fires after the field has been marked as invalid.
33414 * @param {Roo.form.Field} this
33415 * @param {String} msg The validation message
33420 * Fires after the field has been validated with no errors.
33421 * @param {Roo.form.Field} this
33427 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33430 labelAlign : 'top',
33432 dayAllowBlank : false,
33433 monthAllowBlank : false,
33434 yearAllowBlank : false,
33435 dayPlaceholder : '',
33436 monthPlaceholder : '',
33437 yearPlaceholder : '',
33441 isFormField : true,
33447 getAutoCreate : function()
33451 cls : 'row roo-date-split-field-group',
33456 cls : 'form-hidden-field roo-date-split-field-group-value',
33462 var labelCls = 'col-md-12';
33463 var contentCls = 'col-md-4';
33465 if(this.fieldLabel){
33469 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33473 html : this.fieldLabel
33478 if(this.labelAlign == 'left'){
33480 if(this.labelWidth > 12){
33481 label.style = "width: " + this.labelWidth + 'px';
33484 if(this.labelWidth < 13 && this.labelmd == 0){
33485 this.labelmd = this.labelWidth;
33488 if(this.labellg > 0){
33489 labelCls = ' col-lg-' + this.labellg;
33490 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33493 if(this.labelmd > 0){
33494 labelCls = ' col-md-' + this.labelmd;
33495 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33498 if(this.labelsm > 0){
33499 labelCls = ' col-sm-' + this.labelsm;
33500 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33503 if(this.labelxs > 0){
33504 labelCls = ' col-xs-' + this.labelxs;
33505 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33509 label.cls += ' ' + labelCls;
33511 cfg.cn.push(label);
33514 Roo.each(['day', 'month', 'year'], function(t){
33517 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33524 inputEl: function ()
33526 return this.el.select('.roo-date-split-field-group-value', true).first();
33529 onRender : function(ct, position)
33533 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33535 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33537 this.dayField = new Roo.bootstrap.ComboBox({
33538 allowBlank : this.dayAllowBlank,
33539 alwaysQuery : true,
33540 displayField : 'value',
33543 forceSelection : true,
33545 placeholder : this.dayPlaceholder,
33546 selectOnFocus : true,
33547 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33548 triggerAction : 'all',
33550 valueField : 'value',
33551 store : new Roo.data.SimpleStore({
33552 data : (function() {
33554 _this.fireEvent('days', _this, days);
33557 fields : [ 'value' ]
33560 select : function (_self, record, index)
33562 _this.setValue(_this.getValue());
33567 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33569 this.monthField = new Roo.bootstrap.MonthField({
33570 after : '<i class=\"fa fa-calendar\"></i>',
33571 allowBlank : this.monthAllowBlank,
33572 placeholder : this.monthPlaceholder,
33575 render : function (_self)
33577 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33578 e.preventDefault();
33582 select : function (_self, oldvalue, newvalue)
33584 _this.setValue(_this.getValue());
33589 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33591 this.yearField = new Roo.bootstrap.ComboBox({
33592 allowBlank : this.yearAllowBlank,
33593 alwaysQuery : true,
33594 displayField : 'value',
33597 forceSelection : true,
33599 placeholder : this.yearPlaceholder,
33600 selectOnFocus : true,
33601 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33602 triggerAction : 'all',
33604 valueField : 'value',
33605 store : new Roo.data.SimpleStore({
33606 data : (function() {
33608 _this.fireEvent('years', _this, years);
33611 fields : [ 'value' ]
33614 select : function (_self, record, index)
33616 _this.setValue(_this.getValue());
33621 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33624 setValue : function(v, format)
33626 this.inputEl.dom.value = v;
33628 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33630 var d = Date.parseDate(v, f);
33637 this.setDay(d.format(this.dayFormat));
33638 this.setMonth(d.format(this.monthFormat));
33639 this.setYear(d.format(this.yearFormat));
33646 setDay : function(v)
33648 this.dayField.setValue(v);
33649 this.inputEl.dom.value = this.getValue();
33654 setMonth : function(v)
33656 this.monthField.setValue(v, true);
33657 this.inputEl.dom.value = this.getValue();
33662 setYear : function(v)
33664 this.yearField.setValue(v);
33665 this.inputEl.dom.value = this.getValue();
33670 getDay : function()
33672 return this.dayField.getValue();
33675 getMonth : function()
33677 return this.monthField.getValue();
33680 getYear : function()
33682 return this.yearField.getValue();
33685 getValue : function()
33687 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33689 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33699 this.inputEl.dom.value = '';
33704 validate : function()
33706 var d = this.dayField.validate();
33707 var m = this.monthField.validate();
33708 var y = this.yearField.validate();
33713 (!this.dayAllowBlank && !d) ||
33714 (!this.monthAllowBlank && !m) ||
33715 (!this.yearAllowBlank && !y)
33720 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33729 this.markInvalid();
33734 markValid : function()
33737 var label = this.el.select('label', true).first();
33738 var icon = this.el.select('i.fa-star', true).first();
33744 this.fireEvent('valid', this);
33748 * Mark this field as invalid
33749 * @param {String} msg The validation message
33751 markInvalid : function(msg)
33754 var label = this.el.select('label', true).first();
33755 var icon = this.el.select('i.fa-star', true).first();
33757 if(label && !icon){
33758 this.el.select('.roo-date-split-field-label', true).createChild({
33760 cls : 'text-danger fa fa-lg fa-star',
33761 tooltip : 'This field is required',
33762 style : 'margin-right:5px;'
33766 this.fireEvent('invalid', this, msg);
33769 clearInvalid : function()
33771 var label = this.el.select('label', true).first();
33772 var icon = this.el.select('i.fa-star', true).first();
33778 this.fireEvent('valid', this);
33781 getName: function()
33791 * http://masonry.desandro.com
33793 * The idea is to render all the bricks based on vertical width...
33795 * The original code extends 'outlayer' - we might need to use that....
33801 * @class Roo.bootstrap.LayoutMasonry
33802 * @extends Roo.bootstrap.Component
33803 * Bootstrap Layout Masonry class
33806 * Create a new Element
33807 * @param {Object} config The config object
33810 Roo.bootstrap.LayoutMasonry = function(config){
33812 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33816 Roo.bootstrap.LayoutMasonry.register(this);
33822 * Fire after layout the items
33823 * @param {Roo.bootstrap.LayoutMasonry} this
33824 * @param {Roo.EventObject} e
33831 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33834 * @cfg {Boolean} isLayoutInstant = no animation?
33836 isLayoutInstant : false, // needed?
33839 * @cfg {Number} boxWidth width of the columns
33844 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33849 * @cfg {Number} padWidth padding below box..
33854 * @cfg {Number} gutter gutter width..
33859 * @cfg {Number} maxCols maximum number of columns
33865 * @cfg {Boolean} isAutoInitial defalut true
33867 isAutoInitial : true,
33872 * @cfg {Boolean} isHorizontal defalut false
33874 isHorizontal : false,
33876 currentSize : null,
33882 bricks: null, //CompositeElement
33886 _isLayoutInited : false,
33888 // isAlternative : false, // only use for vertical layout...
33891 * @cfg {Number} alternativePadWidth padding below box..
33893 alternativePadWidth : 50,
33895 selectedBrick : [],
33897 getAutoCreate : function(){
33899 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33903 cls: 'blog-masonary-wrapper ' + this.cls,
33905 cls : 'mas-boxes masonary'
33912 getChildContainer: function( )
33914 if (this.boxesEl) {
33915 return this.boxesEl;
33918 this.boxesEl = this.el.select('.mas-boxes').first();
33920 return this.boxesEl;
33924 initEvents : function()
33928 if(this.isAutoInitial){
33929 Roo.log('hook children rendered');
33930 this.on('childrenrendered', function() {
33931 Roo.log('children rendered');
33937 initial : function()
33939 this.selectedBrick = [];
33941 this.currentSize = this.el.getBox(true);
33943 Roo.EventManager.onWindowResize(this.resize, this);
33945 if(!this.isAutoInitial){
33953 //this.layout.defer(500,this);
33957 resize : function()
33959 var cs = this.el.getBox(true);
33962 this.currentSize.width == cs.width &&
33963 this.currentSize.x == cs.x &&
33964 this.currentSize.height == cs.height &&
33965 this.currentSize.y == cs.y
33967 Roo.log("no change in with or X or Y");
33971 this.currentSize = cs;
33977 layout : function()
33979 this._resetLayout();
33981 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33983 this.layoutItems( isInstant );
33985 this._isLayoutInited = true;
33987 this.fireEvent('layout', this);
33991 _resetLayout : function()
33993 if(this.isHorizontal){
33994 this.horizontalMeasureColumns();
33998 this.verticalMeasureColumns();
34002 verticalMeasureColumns : function()
34004 this.getContainerWidth();
34006 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34007 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34011 var boxWidth = this.boxWidth + this.padWidth;
34013 if(this.containerWidth < this.boxWidth){
34014 boxWidth = this.containerWidth
34017 var containerWidth = this.containerWidth;
34019 var cols = Math.floor(containerWidth / boxWidth);
34021 this.cols = Math.max( cols, 1 );
34023 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34025 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34027 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34029 this.colWidth = boxWidth + avail - this.padWidth;
34031 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34032 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34035 horizontalMeasureColumns : function()
34037 this.getContainerWidth();
34039 var boxWidth = this.boxWidth;
34041 if(this.containerWidth < boxWidth){
34042 boxWidth = this.containerWidth;
34045 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34047 this.el.setHeight(boxWidth);
34051 getContainerWidth : function()
34053 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34056 layoutItems : function( isInstant )
34058 Roo.log(this.bricks);
34060 var items = Roo.apply([], this.bricks);
34062 if(this.isHorizontal){
34063 this._horizontalLayoutItems( items , isInstant );
34067 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34068 // this._verticalAlternativeLayoutItems( items , isInstant );
34072 this._verticalLayoutItems( items , isInstant );
34076 _verticalLayoutItems : function ( items , isInstant)
34078 if ( !items || !items.length ) {
34083 ['xs', 'xs', 'xs', 'tall'],
34084 ['xs', 'xs', 'tall'],
34085 ['xs', 'xs', 'sm'],
34086 ['xs', 'xs', 'xs'],
34092 ['sm', 'xs', 'xs'],
34096 ['tall', 'xs', 'xs', 'xs'],
34097 ['tall', 'xs', 'xs'],
34109 Roo.each(items, function(item, k){
34111 switch (item.size) {
34112 // these layouts take up a full box,
34123 boxes.push([item]);
34146 var filterPattern = function(box, length)
34154 var pattern = box.slice(0, length);
34158 Roo.each(pattern, function(i){
34159 format.push(i.size);
34162 Roo.each(standard, function(s){
34164 if(String(s) != String(format)){
34173 if(!match && length == 1){
34178 filterPattern(box, length - 1);
34182 queue.push(pattern);
34184 box = box.slice(length, box.length);
34186 filterPattern(box, 4);
34192 Roo.each(boxes, function(box, k){
34198 if(box.length == 1){
34203 filterPattern(box, 4);
34207 this._processVerticalLayoutQueue( queue, isInstant );
34211 // _verticalAlternativeLayoutItems : function( items , isInstant )
34213 // if ( !items || !items.length ) {
34217 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34221 _horizontalLayoutItems : function ( items , isInstant)
34223 if ( !items || !items.length || items.length < 3) {
34229 var eItems = items.slice(0, 3);
34231 items = items.slice(3, items.length);
34234 ['xs', 'xs', 'xs', 'wide'],
34235 ['xs', 'xs', 'wide'],
34236 ['xs', 'xs', 'sm'],
34237 ['xs', 'xs', 'xs'],
34243 ['sm', 'xs', 'xs'],
34247 ['wide', 'xs', 'xs', 'xs'],
34248 ['wide', 'xs', 'xs'],
34261 Roo.each(items, function(item, k){
34263 switch (item.size) {
34274 boxes.push([item]);
34298 var filterPattern = function(box, length)
34306 var pattern = box.slice(0, length);
34310 Roo.each(pattern, function(i){
34311 format.push(i.size);
34314 Roo.each(standard, function(s){
34316 if(String(s) != String(format)){
34325 if(!match && length == 1){
34330 filterPattern(box, length - 1);
34334 queue.push(pattern);
34336 box = box.slice(length, box.length);
34338 filterPattern(box, 4);
34344 Roo.each(boxes, function(box, k){
34350 if(box.length == 1){
34355 filterPattern(box, 4);
34362 var pos = this.el.getBox(true);
34366 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34368 var hit_end = false;
34370 Roo.each(queue, function(box){
34374 Roo.each(box, function(b){
34376 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34386 Roo.each(box, function(b){
34388 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34391 mx = Math.max(mx, b.x);
34395 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34399 Roo.each(box, function(b){
34401 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34415 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34418 /** Sets position of item in DOM
34419 * @param {Element} item
34420 * @param {Number} x - horizontal position
34421 * @param {Number} y - vertical position
34422 * @param {Boolean} isInstant - disables transitions
34424 _processVerticalLayoutQueue : function( queue, isInstant )
34426 var pos = this.el.getBox(true);
34431 for (var i = 0; i < this.cols; i++){
34435 Roo.each(queue, function(box, k){
34437 var col = k % this.cols;
34439 Roo.each(box, function(b,kk){
34441 b.el.position('absolute');
34443 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34444 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34446 if(b.size == 'md-left' || b.size == 'md-right'){
34447 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34448 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34451 b.el.setWidth(width);
34452 b.el.setHeight(height);
34454 b.el.select('iframe',true).setSize(width,height);
34458 for (var i = 0; i < this.cols; i++){
34460 if(maxY[i] < maxY[col]){
34465 col = Math.min(col, i);
34469 x = pos.x + col * (this.colWidth + this.padWidth);
34473 var positions = [];
34475 switch (box.length){
34477 positions = this.getVerticalOneBoxColPositions(x, y, box);
34480 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34483 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34486 positions = this.getVerticalFourBoxColPositions(x, y, box);
34492 Roo.each(box, function(b,kk){
34494 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34496 var sz = b.el.getSize();
34498 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34506 for (var i = 0; i < this.cols; i++){
34507 mY = Math.max(mY, maxY[i]);
34510 this.el.setHeight(mY - pos.y);
34514 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34516 // var pos = this.el.getBox(true);
34519 // var maxX = pos.right;
34521 // var maxHeight = 0;
34523 // Roo.each(items, function(item, k){
34527 // item.el.position('absolute');
34529 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34531 // item.el.setWidth(width);
34533 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34535 // item.el.setHeight(height);
34538 // item.el.setXY([x, y], isInstant ? false : true);
34540 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34543 // y = y + height + this.alternativePadWidth;
34545 // maxHeight = maxHeight + height + this.alternativePadWidth;
34549 // this.el.setHeight(maxHeight);
34553 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34555 var pos = this.el.getBox(true);
34560 var maxX = pos.right;
34562 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34564 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34566 Roo.each(queue, function(box, k){
34568 Roo.each(box, function(b, kk){
34570 b.el.position('absolute');
34572 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34573 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34575 if(b.size == 'md-left' || b.size == 'md-right'){
34576 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34577 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34580 b.el.setWidth(width);
34581 b.el.setHeight(height);
34589 var positions = [];
34591 switch (box.length){
34593 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34596 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34599 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34602 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34608 Roo.each(box, function(b,kk){
34610 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34612 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34620 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34622 Roo.each(eItems, function(b,k){
34624 b.size = (k == 0) ? 'sm' : 'xs';
34625 b.x = (k == 0) ? 2 : 1;
34626 b.y = (k == 0) ? 2 : 1;
34628 b.el.position('absolute');
34630 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34632 b.el.setWidth(width);
34634 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34636 b.el.setHeight(height);
34640 var positions = [];
34643 x : maxX - this.unitWidth * 2 - this.gutter,
34648 x : maxX - this.unitWidth,
34649 y : minY + (this.unitWidth + this.gutter) * 2
34653 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34657 Roo.each(eItems, function(b,k){
34659 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34665 getVerticalOneBoxColPositions : function(x, y, box)
34669 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34671 if(box[0].size == 'md-left'){
34675 if(box[0].size == 'md-right'){
34680 x : x + (this.unitWidth + this.gutter) * rand,
34687 getVerticalTwoBoxColPositions : function(x, y, box)
34691 if(box[0].size == 'xs'){
34695 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34699 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34713 x : x + (this.unitWidth + this.gutter) * 2,
34714 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34721 getVerticalThreeBoxColPositions : function(x, y, box)
34725 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34733 x : x + (this.unitWidth + this.gutter) * 1,
34738 x : x + (this.unitWidth + this.gutter) * 2,
34746 if(box[0].size == 'xs' && box[1].size == 'xs'){
34755 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34759 x : x + (this.unitWidth + this.gutter) * 1,
34773 x : x + (this.unitWidth + this.gutter) * 2,
34778 x : x + (this.unitWidth + this.gutter) * 2,
34779 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34786 getVerticalFourBoxColPositions : function(x, y, box)
34790 if(box[0].size == 'xs'){
34799 y : y + (this.unitHeight + this.gutter) * 1
34804 y : y + (this.unitHeight + this.gutter) * 2
34808 x : x + (this.unitWidth + this.gutter) * 1,
34822 x : x + (this.unitWidth + this.gutter) * 2,
34827 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34828 y : y + (this.unitHeight + this.gutter) * 1
34832 x : x + (this.unitWidth + this.gutter) * 2,
34833 y : y + (this.unitWidth + this.gutter) * 2
34840 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34844 if(box[0].size == 'md-left'){
34846 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34853 if(box[0].size == 'md-right'){
34855 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34856 y : minY + (this.unitWidth + this.gutter) * 1
34862 var rand = Math.floor(Math.random() * (4 - box[0].y));
34865 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34866 y : minY + (this.unitWidth + this.gutter) * rand
34873 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34877 if(box[0].size == 'xs'){
34880 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34885 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34886 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34894 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34899 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34900 y : minY + (this.unitWidth + this.gutter) * 2
34907 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34911 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34914 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34919 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34920 y : minY + (this.unitWidth + this.gutter) * 1
34924 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34925 y : minY + (this.unitWidth + this.gutter) * 2
34932 if(box[0].size == 'xs' && box[1].size == 'xs'){
34935 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34940 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34945 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34946 y : minY + (this.unitWidth + this.gutter) * 1
34954 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34959 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34960 y : minY + (this.unitWidth + this.gutter) * 2
34964 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34965 y : minY + (this.unitWidth + this.gutter) * 2
34972 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34976 if(box[0].size == 'xs'){
34979 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34984 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34989 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),
34994 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34995 y : minY + (this.unitWidth + this.gutter) * 1
35003 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35008 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35009 y : minY + (this.unitWidth + this.gutter) * 2
35013 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35014 y : minY + (this.unitWidth + this.gutter) * 2
35018 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),
35019 y : minY + (this.unitWidth + this.gutter) * 2
35027 * remove a Masonry Brick
35028 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35030 removeBrick : function(brick_id)
35036 for (var i = 0; i<this.bricks.length; i++) {
35037 if (this.bricks[i].id == brick_id) {
35038 this.bricks.splice(i,1);
35039 this.el.dom.removeChild(Roo.get(brick_id).dom);
35046 * adds a Masonry Brick
35047 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35049 addBrick : function(cfg)
35051 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35052 //this.register(cn);
35053 cn.parentId = this.id;
35054 cn.render(this.el);
35059 * register a Masonry Brick
35060 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35063 register : function(brick)
35065 this.bricks.push(brick);
35066 brick.masonryId = this.id;
35070 * clear all the Masonry Brick
35072 clearAll : function()
35075 //this.getChildContainer().dom.innerHTML = "";
35076 this.el.dom.innerHTML = '';
35079 getSelected : function()
35081 if (!this.selectedBrick) {
35085 return this.selectedBrick;
35089 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35093 * register a Masonry Layout
35094 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35097 register : function(layout)
35099 this.groups[layout.id] = layout;
35102 * fetch a Masonry Layout based on the masonry layout ID
35103 * @param {string} the masonry layout to add
35104 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35107 get: function(layout_id) {
35108 if (typeof(this.groups[layout_id]) == 'undefined') {
35111 return this.groups[layout_id] ;
35123 * http://masonry.desandro.com
35125 * The idea is to render all the bricks based on vertical width...
35127 * The original code extends 'outlayer' - we might need to use that....
35133 * @class Roo.bootstrap.LayoutMasonryAuto
35134 * @extends Roo.bootstrap.Component
35135 * Bootstrap Layout Masonry class
35138 * Create a new Element
35139 * @param {Object} config The config object
35142 Roo.bootstrap.LayoutMasonryAuto = function(config){
35143 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35146 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35149 * @cfg {Boolean} isFitWidth - resize the width..
35151 isFitWidth : false, // options..
35153 * @cfg {Boolean} isOriginLeft = left align?
35155 isOriginLeft : true,
35157 * @cfg {Boolean} isOriginTop = top align?
35159 isOriginTop : false,
35161 * @cfg {Boolean} isLayoutInstant = no animation?
35163 isLayoutInstant : false, // needed?
35165 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35167 isResizingContainer : true,
35169 * @cfg {Number} columnWidth width of the columns
35175 * @cfg {Number} maxCols maximum number of columns
35180 * @cfg {Number} padHeight padding below box..
35186 * @cfg {Boolean} isAutoInitial defalut true
35189 isAutoInitial : true,
35195 initialColumnWidth : 0,
35196 currentSize : null,
35198 colYs : null, // array.
35205 bricks: null, //CompositeElement
35206 cols : 0, // array?
35207 // element : null, // wrapped now this.el
35208 _isLayoutInited : null,
35211 getAutoCreate : function(){
35215 cls: 'blog-masonary-wrapper ' + this.cls,
35217 cls : 'mas-boxes masonary'
35224 getChildContainer: function( )
35226 if (this.boxesEl) {
35227 return this.boxesEl;
35230 this.boxesEl = this.el.select('.mas-boxes').first();
35232 return this.boxesEl;
35236 initEvents : function()
35240 if(this.isAutoInitial){
35241 Roo.log('hook children rendered');
35242 this.on('childrenrendered', function() {
35243 Roo.log('children rendered');
35250 initial : function()
35252 this.reloadItems();
35254 this.currentSize = this.el.getBox(true);
35256 /// was window resize... - let's see if this works..
35257 Roo.EventManager.onWindowResize(this.resize, this);
35259 if(!this.isAutoInitial){
35264 this.layout.defer(500,this);
35267 reloadItems: function()
35269 this.bricks = this.el.select('.masonry-brick', true);
35271 this.bricks.each(function(b) {
35272 //Roo.log(b.getSize());
35273 if (!b.attr('originalwidth')) {
35274 b.attr('originalwidth', b.getSize().width);
35279 Roo.log(this.bricks.elements.length);
35282 resize : function()
35285 var cs = this.el.getBox(true);
35287 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35288 Roo.log("no change in with or X");
35291 this.currentSize = cs;
35295 layout : function()
35298 this._resetLayout();
35299 //this._manageStamps();
35301 // don't animate first layout
35302 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35303 this.layoutItems( isInstant );
35305 // flag for initalized
35306 this._isLayoutInited = true;
35309 layoutItems : function( isInstant )
35311 //var items = this._getItemsForLayout( this.items );
35312 // original code supports filtering layout items.. we just ignore it..
35314 this._layoutItems( this.bricks , isInstant );
35316 this._postLayout();
35318 _layoutItems : function ( items , isInstant)
35320 //this.fireEvent( 'layout', this, items );
35323 if ( !items || !items.elements.length ) {
35324 // no items, emit event with empty array
35329 items.each(function(item) {
35330 Roo.log("layout item");
35332 // get x/y object from method
35333 var position = this._getItemLayoutPosition( item );
35335 position.item = item;
35336 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35337 queue.push( position );
35340 this._processLayoutQueue( queue );
35342 /** Sets position of item in DOM
35343 * @param {Element} item
35344 * @param {Number} x - horizontal position
35345 * @param {Number} y - vertical position
35346 * @param {Boolean} isInstant - disables transitions
35348 _processLayoutQueue : function( queue )
35350 for ( var i=0, len = queue.length; i < len; i++ ) {
35351 var obj = queue[i];
35352 obj.item.position('absolute');
35353 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35359 * Any logic you want to do after each layout,
35360 * i.e. size the container
35362 _postLayout : function()
35364 this.resizeContainer();
35367 resizeContainer : function()
35369 if ( !this.isResizingContainer ) {
35372 var size = this._getContainerSize();
35374 this.el.setSize(size.width,size.height);
35375 this.boxesEl.setSize(size.width,size.height);
35381 _resetLayout : function()
35383 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35384 this.colWidth = this.el.getWidth();
35385 //this.gutter = this.el.getWidth();
35387 this.measureColumns();
35393 this.colYs.push( 0 );
35399 measureColumns : function()
35401 this.getContainerWidth();
35402 // if columnWidth is 0, default to outerWidth of first item
35403 if ( !this.columnWidth ) {
35404 var firstItem = this.bricks.first();
35405 Roo.log(firstItem);
35406 this.columnWidth = this.containerWidth;
35407 if (firstItem && firstItem.attr('originalwidth') ) {
35408 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35410 // columnWidth fall back to item of first element
35411 Roo.log("set column width?");
35412 this.initialColumnWidth = this.columnWidth ;
35414 // if first elem has no width, default to size of container
35419 if (this.initialColumnWidth) {
35420 this.columnWidth = this.initialColumnWidth;
35425 // column width is fixed at the top - however if container width get's smaller we should
35428 // this bit calcs how man columns..
35430 var columnWidth = this.columnWidth += this.gutter;
35432 // calculate columns
35433 var containerWidth = this.containerWidth + this.gutter;
35435 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35436 // fix rounding errors, typically with gutters
35437 var excess = columnWidth - containerWidth % columnWidth;
35440 // if overshoot is less than a pixel, round up, otherwise floor it
35441 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35442 cols = Math[ mathMethod ]( cols );
35443 this.cols = Math.max( cols, 1 );
35444 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35446 // padding positioning..
35447 var totalColWidth = this.cols * this.columnWidth;
35448 var padavail = this.containerWidth - totalColWidth;
35449 // so for 2 columns - we need 3 'pads'
35451 var padNeeded = (1+this.cols) * this.padWidth;
35453 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35455 this.columnWidth += padExtra
35456 //this.padWidth = Math.floor(padavail / ( this.cols));
35458 // adjust colum width so that padding is fixed??
35460 // we have 3 columns ... total = width * 3
35461 // we have X left over... that should be used by
35463 //if (this.expandC) {
35471 getContainerWidth : function()
35473 /* // container is parent if fit width
35474 var container = this.isFitWidth ? this.element.parentNode : this.element;
35475 // check that this.size and size are there
35476 // IE8 triggers resize on body size change, so they might not be
35478 var size = getSize( container ); //FIXME
35479 this.containerWidth = size && size.innerWidth; //FIXME
35482 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35486 _getItemLayoutPosition : function( item ) // what is item?
35488 // we resize the item to our columnWidth..
35490 item.setWidth(this.columnWidth);
35491 item.autoBoxAdjust = false;
35493 var sz = item.getSize();
35495 // how many columns does this brick span
35496 var remainder = this.containerWidth % this.columnWidth;
35498 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35499 // round if off by 1 pixel, otherwise use ceil
35500 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35501 colSpan = Math.min( colSpan, this.cols );
35503 // normally this should be '1' as we dont' currently allow multi width columns..
35505 var colGroup = this._getColGroup( colSpan );
35506 // get the minimum Y value from the columns
35507 var minimumY = Math.min.apply( Math, colGroup );
35508 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35510 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35512 // position the brick
35514 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35515 y: this.currentSize.y + minimumY + this.padHeight
35519 // apply setHeight to necessary columns
35520 var setHeight = minimumY + sz.height + this.padHeight;
35521 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35523 var setSpan = this.cols + 1 - colGroup.length;
35524 for ( var i = 0; i < setSpan; i++ ) {
35525 this.colYs[ shortColIndex + i ] = setHeight ;
35532 * @param {Number} colSpan - number of columns the element spans
35533 * @returns {Array} colGroup
35535 _getColGroup : function( colSpan )
35537 if ( colSpan < 2 ) {
35538 // if brick spans only one column, use all the column Ys
35543 // how many different places could this brick fit horizontally
35544 var groupCount = this.cols + 1 - colSpan;
35545 // for each group potential horizontal position
35546 for ( var i = 0; i < groupCount; i++ ) {
35547 // make an array of colY values for that one group
35548 var groupColYs = this.colYs.slice( i, i + colSpan );
35549 // and get the max value of the array
35550 colGroup[i] = Math.max.apply( Math, groupColYs );
35555 _manageStamp : function( stamp )
35557 var stampSize = stamp.getSize();
35558 var offset = stamp.getBox();
35559 // get the columns that this stamp affects
35560 var firstX = this.isOriginLeft ? offset.x : offset.right;
35561 var lastX = firstX + stampSize.width;
35562 var firstCol = Math.floor( firstX / this.columnWidth );
35563 firstCol = Math.max( 0, firstCol );
35565 var lastCol = Math.floor( lastX / this.columnWidth );
35566 // lastCol should not go over if multiple of columnWidth #425
35567 lastCol -= lastX % this.columnWidth ? 0 : 1;
35568 lastCol = Math.min( this.cols - 1, lastCol );
35570 // set colYs to bottom of the stamp
35571 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35574 for ( var i = firstCol; i <= lastCol; i++ ) {
35575 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35580 _getContainerSize : function()
35582 this.maxY = Math.max.apply( Math, this.colYs );
35587 if ( this.isFitWidth ) {
35588 size.width = this._getContainerFitWidth();
35594 _getContainerFitWidth : function()
35596 var unusedCols = 0;
35597 // count unused columns
35600 if ( this.colYs[i] !== 0 ) {
35605 // fit container to columns that have been used
35606 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35609 needsResizeLayout : function()
35611 var previousWidth = this.containerWidth;
35612 this.getContainerWidth();
35613 return previousWidth !== this.containerWidth;
35628 * @class Roo.bootstrap.MasonryBrick
35629 * @extends Roo.bootstrap.Component
35630 * Bootstrap MasonryBrick class
35633 * Create a new MasonryBrick
35634 * @param {Object} config The config object
35637 Roo.bootstrap.MasonryBrick = function(config){
35639 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35641 Roo.bootstrap.MasonryBrick.register(this);
35647 * When a MasonryBrick is clcik
35648 * @param {Roo.bootstrap.MasonryBrick} this
35649 * @param {Roo.EventObject} e
35655 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35658 * @cfg {String} title
35662 * @cfg {String} html
35666 * @cfg {String} bgimage
35670 * @cfg {String} videourl
35674 * @cfg {String} cls
35678 * @cfg {String} href
35682 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35687 * @cfg {String} placetitle (center|bottom)
35692 * @cfg {Boolean} isFitContainer defalut true
35694 isFitContainer : true,
35697 * @cfg {Boolean} preventDefault defalut false
35699 preventDefault : false,
35702 * @cfg {Boolean} inverse defalut false
35704 maskInverse : false,
35706 getAutoCreate : function()
35708 if(!this.isFitContainer){
35709 return this.getSplitAutoCreate();
35712 var cls = 'masonry-brick masonry-brick-full';
35714 if(this.href.length){
35715 cls += ' masonry-brick-link';
35718 if(this.bgimage.length){
35719 cls += ' masonry-brick-image';
35722 if(this.maskInverse){
35723 cls += ' mask-inverse';
35726 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35727 cls += ' enable-mask';
35731 cls += ' masonry-' + this.size + '-brick';
35734 if(this.placetitle.length){
35736 switch (this.placetitle) {
35738 cls += ' masonry-center-title';
35741 cls += ' masonry-bottom-title';
35748 if(!this.html.length && !this.bgimage.length){
35749 cls += ' masonry-center-title';
35752 if(!this.html.length && this.bgimage.length){
35753 cls += ' masonry-bottom-title';
35758 cls += ' ' + this.cls;
35762 tag: (this.href.length) ? 'a' : 'div',
35767 cls: 'masonry-brick-mask'
35771 cls: 'masonry-brick-paragraph',
35777 if(this.href.length){
35778 cfg.href = this.href;
35781 var cn = cfg.cn[1].cn;
35783 if(this.title.length){
35786 cls: 'masonry-brick-title',
35791 if(this.html.length){
35794 cls: 'masonry-brick-text',
35799 if (!this.title.length && !this.html.length) {
35800 cfg.cn[1].cls += ' hide';
35803 if(this.bgimage.length){
35806 cls: 'masonry-brick-image-view',
35811 if(this.videourl.length){
35812 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35813 // youtube support only?
35816 cls: 'masonry-brick-image-view',
35819 allowfullscreen : true
35827 getSplitAutoCreate : function()
35829 var cls = 'masonry-brick masonry-brick-split';
35831 if(this.href.length){
35832 cls += ' masonry-brick-link';
35835 if(this.bgimage.length){
35836 cls += ' masonry-brick-image';
35840 cls += ' masonry-' + this.size + '-brick';
35843 switch (this.placetitle) {
35845 cls += ' masonry-center-title';
35848 cls += ' masonry-bottom-title';
35851 if(!this.bgimage.length){
35852 cls += ' masonry-center-title';
35855 if(this.bgimage.length){
35856 cls += ' masonry-bottom-title';
35862 cls += ' ' + this.cls;
35866 tag: (this.href.length) ? 'a' : 'div',
35871 cls: 'masonry-brick-split-head',
35875 cls: 'masonry-brick-paragraph',
35882 cls: 'masonry-brick-split-body',
35888 if(this.href.length){
35889 cfg.href = this.href;
35892 if(this.title.length){
35893 cfg.cn[0].cn[0].cn.push({
35895 cls: 'masonry-brick-title',
35900 if(this.html.length){
35901 cfg.cn[1].cn.push({
35903 cls: 'masonry-brick-text',
35908 if(this.bgimage.length){
35909 cfg.cn[0].cn.push({
35911 cls: 'masonry-brick-image-view',
35916 if(this.videourl.length){
35917 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35918 // youtube support only?
35919 cfg.cn[0].cn.cn.push({
35921 cls: 'masonry-brick-image-view',
35924 allowfullscreen : true
35931 initEvents: function()
35933 switch (this.size) {
35966 this.el.on('touchstart', this.onTouchStart, this);
35967 this.el.on('touchmove', this.onTouchMove, this);
35968 this.el.on('touchend', this.onTouchEnd, this);
35969 this.el.on('contextmenu', this.onContextMenu, this);
35971 this.el.on('mouseenter' ,this.enter, this);
35972 this.el.on('mouseleave', this.leave, this);
35973 this.el.on('click', this.onClick, this);
35976 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35977 this.parent().bricks.push(this);
35982 onClick: function(e, el)
35984 var time = this.endTimer - this.startTimer;
35985 // Roo.log(e.preventDefault());
35988 e.preventDefault();
35993 if(!this.preventDefault){
35997 e.preventDefault();
35999 if (this.activeClass != '') {
36000 this.selectBrick();
36003 this.fireEvent('click', this, e);
36006 enter: function(e, el)
36008 e.preventDefault();
36010 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36014 if(this.bgimage.length && this.html.length){
36015 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36019 leave: function(e, el)
36021 e.preventDefault();
36023 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36027 if(this.bgimage.length && this.html.length){
36028 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36032 onTouchStart: function(e, el)
36034 // e.preventDefault();
36036 this.touchmoved = false;
36038 if(!this.isFitContainer){
36042 if(!this.bgimage.length || !this.html.length){
36046 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36048 this.timer = new Date().getTime();
36052 onTouchMove: function(e, el)
36054 this.touchmoved = true;
36057 onContextMenu : function(e,el)
36059 e.preventDefault();
36060 e.stopPropagation();
36064 onTouchEnd: function(e, el)
36066 // e.preventDefault();
36068 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36075 if(!this.bgimage.length || !this.html.length){
36077 if(this.href.length){
36078 window.location.href = this.href;
36084 if(!this.isFitContainer){
36088 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36090 window.location.href = this.href;
36093 //selection on single brick only
36094 selectBrick : function() {
36096 if (!this.parentId) {
36100 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36101 var index = m.selectedBrick.indexOf(this.id);
36104 m.selectedBrick.splice(index,1);
36105 this.el.removeClass(this.activeClass);
36109 for(var i = 0; i < m.selectedBrick.length; i++) {
36110 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36111 b.el.removeClass(b.activeClass);
36114 m.selectedBrick = [];
36116 m.selectedBrick.push(this.id);
36117 this.el.addClass(this.activeClass);
36121 isSelected : function(){
36122 return this.el.hasClass(this.activeClass);
36127 Roo.apply(Roo.bootstrap.MasonryBrick, {
36130 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36132 * register a Masonry Brick
36133 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36136 register : function(brick)
36138 //this.groups[brick.id] = brick;
36139 this.groups.add(brick.id, brick);
36142 * fetch a masonry brick based on the masonry brick ID
36143 * @param {string} the masonry brick to add
36144 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36147 get: function(brick_id)
36149 // if (typeof(this.groups[brick_id]) == 'undefined') {
36152 // return this.groups[brick_id] ;
36154 if(this.groups.key(brick_id)) {
36155 return this.groups.key(brick_id);
36173 * @class Roo.bootstrap.Brick
36174 * @extends Roo.bootstrap.Component
36175 * Bootstrap Brick class
36178 * Create a new Brick
36179 * @param {Object} config The config object
36182 Roo.bootstrap.Brick = function(config){
36183 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36189 * When a Brick is click
36190 * @param {Roo.bootstrap.Brick} this
36191 * @param {Roo.EventObject} e
36197 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36200 * @cfg {String} title
36204 * @cfg {String} html
36208 * @cfg {String} bgimage
36212 * @cfg {String} cls
36216 * @cfg {String} href
36220 * @cfg {String} video
36224 * @cfg {Boolean} square
36228 getAutoCreate : function()
36230 var cls = 'roo-brick';
36232 if(this.href.length){
36233 cls += ' roo-brick-link';
36236 if(this.bgimage.length){
36237 cls += ' roo-brick-image';
36240 if(!this.html.length && !this.bgimage.length){
36241 cls += ' roo-brick-center-title';
36244 if(!this.html.length && this.bgimage.length){
36245 cls += ' roo-brick-bottom-title';
36249 cls += ' ' + this.cls;
36253 tag: (this.href.length) ? 'a' : 'div',
36258 cls: 'roo-brick-paragraph',
36264 if(this.href.length){
36265 cfg.href = this.href;
36268 var cn = cfg.cn[0].cn;
36270 if(this.title.length){
36273 cls: 'roo-brick-title',
36278 if(this.html.length){
36281 cls: 'roo-brick-text',
36288 if(this.bgimage.length){
36291 cls: 'roo-brick-image-view',
36299 initEvents: function()
36301 if(this.title.length || this.html.length){
36302 this.el.on('mouseenter' ,this.enter, this);
36303 this.el.on('mouseleave', this.leave, this);
36306 Roo.EventManager.onWindowResize(this.resize, this);
36308 if(this.bgimage.length){
36309 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36310 this.imageEl.on('load', this.onImageLoad, this);
36317 onImageLoad : function()
36322 resize : function()
36324 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36326 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36328 if(this.bgimage.length){
36329 var image = this.el.select('.roo-brick-image-view', true).first();
36331 image.setWidth(paragraph.getWidth());
36334 image.setHeight(paragraph.getWidth());
36337 this.el.setHeight(image.getHeight());
36338 paragraph.setHeight(image.getHeight());
36344 enter: function(e, el)
36346 e.preventDefault();
36348 if(this.bgimage.length){
36349 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36350 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36354 leave: function(e, el)
36356 e.preventDefault();
36358 if(this.bgimage.length){
36359 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36360 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36375 * @class Roo.bootstrap.NumberField
36376 * @extends Roo.bootstrap.Input
36377 * Bootstrap NumberField class
36383 * Create a new NumberField
36384 * @param {Object} config The config object
36387 Roo.bootstrap.NumberField = function(config){
36388 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36391 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36394 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36396 allowDecimals : true,
36398 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36400 decimalSeparator : ".",
36402 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36404 decimalPrecision : 2,
36406 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36408 allowNegative : true,
36411 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36415 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36417 minValue : Number.NEGATIVE_INFINITY,
36419 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36421 maxValue : Number.MAX_VALUE,
36423 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36425 minText : "The minimum value for this field is {0}",
36427 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36429 maxText : "The maximum value for this field is {0}",
36431 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36432 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36434 nanText : "{0} is not a valid number",
36436 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36438 thousandsDelimiter : false,
36440 * @cfg {String} valueAlign alignment of value
36442 valueAlign : "left",
36444 getAutoCreate : function()
36446 var hiddenInput = {
36450 cls: 'hidden-number-input'
36454 hiddenInput.name = this.name;
36459 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36461 this.name = hiddenInput.name;
36463 if(cfg.cn.length > 0) {
36464 cfg.cn.push(hiddenInput);
36471 initEvents : function()
36473 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36475 var allowed = "0123456789";
36477 if(this.allowDecimals){
36478 allowed += this.decimalSeparator;
36481 if(this.allowNegative){
36485 if(this.thousandsDelimiter) {
36489 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36491 var keyPress = function(e){
36493 var k = e.getKey();
36495 var c = e.getCharCode();
36498 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36499 allowed.indexOf(String.fromCharCode(c)) === -1
36505 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36509 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36514 this.el.on("keypress", keyPress, this);
36517 validateValue : function(value)
36520 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36524 var num = this.parseValue(value);
36527 this.markInvalid(String.format(this.nanText, value));
36531 if(num < this.minValue){
36532 this.markInvalid(String.format(this.minText, this.minValue));
36536 if(num > this.maxValue){
36537 this.markInvalid(String.format(this.maxText, this.maxValue));
36544 getValue : function()
36546 var v = this.hiddenEl().getValue();
36548 return this.fixPrecision(this.parseValue(v));
36551 parseValue : function(value)
36553 if(this.thousandsDelimiter) {
36555 r = new RegExp(",", "g");
36556 value = value.replace(r, "");
36559 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36560 return isNaN(value) ? '' : value;
36563 fixPrecision : function(value)
36565 if(this.thousandsDelimiter) {
36567 r = new RegExp(",", "g");
36568 value = value.replace(r, "");
36571 var nan = isNaN(value);
36573 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36574 return nan ? '' : value;
36576 return parseFloat(value).toFixed(this.decimalPrecision);
36579 setValue : function(v)
36581 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36587 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36589 this.inputEl().dom.value = (v == '') ? '' :
36590 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36592 if(!this.allowZero && v === '0') {
36593 this.hiddenEl().dom.value = '';
36594 this.inputEl().dom.value = '';
36601 decimalPrecisionFcn : function(v)
36603 return Math.floor(v);
36606 beforeBlur : function()
36608 var v = this.parseValue(this.getRawValue());
36610 if(v || v === 0 || v === ''){
36615 hiddenEl : function()
36617 return this.el.select('input.hidden-number-input',true).first();
36629 * @class Roo.bootstrap.DocumentSlider
36630 * @extends Roo.bootstrap.Component
36631 * Bootstrap DocumentSlider class
36634 * Create a new DocumentViewer
36635 * @param {Object} config The config object
36638 Roo.bootstrap.DocumentSlider = function(config){
36639 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36646 * Fire after initEvent
36647 * @param {Roo.bootstrap.DocumentSlider} this
36652 * Fire after update
36653 * @param {Roo.bootstrap.DocumentSlider} this
36659 * @param {Roo.bootstrap.DocumentSlider} this
36665 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36671 getAutoCreate : function()
36675 cls : 'roo-document-slider',
36679 cls : 'roo-document-slider-header',
36683 cls : 'roo-document-slider-header-title'
36689 cls : 'roo-document-slider-body',
36693 cls : 'roo-document-slider-prev',
36697 cls : 'fa fa-chevron-left'
36703 cls : 'roo-document-slider-thumb',
36707 cls : 'roo-document-slider-image'
36713 cls : 'roo-document-slider-next',
36717 cls : 'fa fa-chevron-right'
36729 initEvents : function()
36731 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36732 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36734 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36735 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36737 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36738 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36740 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36741 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36743 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36744 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36746 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36747 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36749 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36750 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36752 this.thumbEl.on('click', this.onClick, this);
36754 this.prevIndicator.on('click', this.prev, this);
36756 this.nextIndicator.on('click', this.next, this);
36760 initial : function()
36762 if(this.files.length){
36763 this.indicator = 1;
36767 this.fireEvent('initial', this);
36770 update : function()
36772 this.imageEl.attr('src', this.files[this.indicator - 1]);
36774 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36776 this.prevIndicator.show();
36778 if(this.indicator == 1){
36779 this.prevIndicator.hide();
36782 this.nextIndicator.show();
36784 if(this.indicator == this.files.length){
36785 this.nextIndicator.hide();
36788 this.thumbEl.scrollTo('top');
36790 this.fireEvent('update', this);
36793 onClick : function(e)
36795 e.preventDefault();
36797 this.fireEvent('click', this);
36802 e.preventDefault();
36804 this.indicator = Math.max(1, this.indicator - 1);
36811 e.preventDefault();
36813 this.indicator = Math.min(this.files.length, this.indicator + 1);
36827 * @class Roo.bootstrap.RadioSet
36828 * @extends Roo.bootstrap.Input
36829 * Bootstrap RadioSet class
36830 * @cfg {String} indicatorpos (left|right) default left
36831 * @cfg {Boolean} inline (true|false) inline the element (default true)
36832 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36834 * Create a new RadioSet
36835 * @param {Object} config The config object
36838 Roo.bootstrap.RadioSet = function(config){
36840 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36844 Roo.bootstrap.RadioSet.register(this);
36849 * Fires when the element is checked or unchecked.
36850 * @param {Roo.bootstrap.RadioSet} this This radio
36851 * @param {Roo.bootstrap.Radio} item The checked item
36856 * Fires when the element is click.
36857 * @param {Roo.bootstrap.RadioSet} this This radio set
36858 * @param {Roo.bootstrap.Radio} item The checked item
36859 * @param {Roo.EventObject} e The event object
36866 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36874 indicatorpos : 'left',
36876 getAutoCreate : function()
36880 cls : 'roo-radio-set-label',
36884 html : this.fieldLabel
36888 if (Roo.bootstrap.version == 3) {
36891 if(this.indicatorpos == 'left'){
36894 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36895 tooltip : 'This field is required'
36900 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36901 tooltip : 'This field is required'
36907 cls : 'roo-radio-set-items'
36910 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36912 if (align === 'left' && this.fieldLabel.length) {
36915 cls : "roo-radio-set-right",
36921 if(this.labelWidth > 12){
36922 label.style = "width: " + this.labelWidth + 'px';
36925 if(this.labelWidth < 13 && this.labelmd == 0){
36926 this.labelmd = this.labelWidth;
36929 if(this.labellg > 0){
36930 label.cls += ' col-lg-' + this.labellg;
36931 items.cls += ' col-lg-' + (12 - this.labellg);
36934 if(this.labelmd > 0){
36935 label.cls += ' col-md-' + this.labelmd;
36936 items.cls += ' col-md-' + (12 - this.labelmd);
36939 if(this.labelsm > 0){
36940 label.cls += ' col-sm-' + this.labelsm;
36941 items.cls += ' col-sm-' + (12 - this.labelsm);
36944 if(this.labelxs > 0){
36945 label.cls += ' col-xs-' + this.labelxs;
36946 items.cls += ' col-xs-' + (12 - this.labelxs);
36952 cls : 'roo-radio-set',
36956 cls : 'roo-radio-set-input',
36959 value : this.value ? this.value : ''
36966 if(this.weight.length){
36967 cfg.cls += ' roo-radio-' + this.weight;
36971 cfg.cls += ' roo-radio-set-inline';
36975 ['xs','sm','md','lg'].map(function(size){
36976 if (settings[size]) {
36977 cfg.cls += ' col-' + size + '-' + settings[size];
36985 initEvents : function()
36987 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36988 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36990 if(!this.fieldLabel.length){
36991 this.labelEl.hide();
36994 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36995 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36997 this.indicator = this.indicatorEl();
36999 if(this.indicator){
37000 this.indicator.addClass('invisible');
37003 this.originalValue = this.getValue();
37007 inputEl: function ()
37009 return this.el.select('.roo-radio-set-input', true).first();
37012 getChildContainer : function()
37014 return this.itemsEl;
37017 register : function(item)
37019 this.radioes.push(item);
37023 validate : function()
37025 if(this.getVisibilityEl().hasClass('hidden')){
37031 Roo.each(this.radioes, function(i){
37040 if(this.allowBlank) {
37044 if(this.disabled || valid){
37049 this.markInvalid();
37054 markValid : function()
37056 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37057 this.indicatorEl().removeClass('visible');
37058 this.indicatorEl().addClass('invisible');
37062 if (Roo.bootstrap.version == 3) {
37063 this.el.removeClass([this.invalidClass, this.validClass]);
37064 this.el.addClass(this.validClass);
37066 this.el.removeClass(['is-invalid','is-valid']);
37067 this.el.addClass(['is-valid']);
37069 this.fireEvent('valid', this);
37072 markInvalid : function(msg)
37074 if(this.allowBlank || this.disabled){
37078 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37079 this.indicatorEl().removeClass('invisible');
37080 this.indicatorEl().addClass('visible');
37082 if (Roo.bootstrap.version == 3) {
37083 this.el.removeClass([this.invalidClass, this.validClass]);
37084 this.el.addClass(this.invalidClass);
37086 this.el.removeClass(['is-invalid','is-valid']);
37087 this.el.addClass(['is-invalid']);
37090 this.fireEvent('invalid', this, msg);
37094 setValue : function(v, suppressEvent)
37096 if(this.value === v){
37103 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37106 Roo.each(this.radioes, function(i){
37108 i.el.removeClass('checked');
37111 Roo.each(this.radioes, function(i){
37113 if(i.value === v || i.value.toString() === v.toString()){
37115 i.el.addClass('checked');
37117 if(suppressEvent !== true){
37118 this.fireEvent('check', this, i);
37129 clearInvalid : function(){
37131 if(!this.el || this.preventMark){
37135 this.el.removeClass([this.invalidClass]);
37137 this.fireEvent('valid', this);
37142 Roo.apply(Roo.bootstrap.RadioSet, {
37146 register : function(set)
37148 this.groups[set.name] = set;
37151 get: function(name)
37153 if (typeof(this.groups[name]) == 'undefined') {
37157 return this.groups[name] ;
37163 * Ext JS Library 1.1.1
37164 * Copyright(c) 2006-2007, Ext JS, LLC.
37166 * Originally Released Under LGPL - original licence link has changed is not relivant.
37169 * <script type="text/javascript">
37174 * @class Roo.bootstrap.SplitBar
37175 * @extends Roo.util.Observable
37176 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37180 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37181 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37182 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37183 split.minSize = 100;
37184 split.maxSize = 600;
37185 split.animate = true;
37186 split.on('moved', splitterMoved);
37189 * Create a new SplitBar
37190 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37191 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37192 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37193 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37194 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37195 position of the SplitBar).
37197 Roo.bootstrap.SplitBar = function(cfg){
37202 // dragElement : elm
37203 // resizingElement: el,
37205 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37206 // placement : Roo.bootstrap.SplitBar.LEFT ,
37207 // existingProxy ???
37210 this.el = Roo.get(cfg.dragElement, true);
37211 this.el.dom.unselectable = "on";
37213 this.resizingEl = Roo.get(cfg.resizingElement, true);
37217 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37218 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37221 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37224 * The minimum size of the resizing element. (Defaults to 0)
37230 * The maximum size of the resizing element. (Defaults to 2000)
37233 this.maxSize = 2000;
37236 * Whether to animate the transition to the new size
37239 this.animate = false;
37242 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37245 this.useShim = false;
37250 if(!cfg.existingProxy){
37252 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37254 this.proxy = Roo.get(cfg.existingProxy).dom;
37257 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37260 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37263 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37266 this.dragSpecs = {};
37269 * @private The adapter to use to positon and resize elements
37271 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37272 this.adapter.init(this);
37274 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37276 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37277 this.el.addClass("roo-splitbar-h");
37280 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37281 this.el.addClass("roo-splitbar-v");
37287 * Fires when the splitter is moved (alias for {@link #event-moved})
37288 * @param {Roo.bootstrap.SplitBar} this
37289 * @param {Number} newSize the new width or height
37294 * Fires when the splitter is moved
37295 * @param {Roo.bootstrap.SplitBar} this
37296 * @param {Number} newSize the new width or height
37300 * @event beforeresize
37301 * Fires before the splitter is dragged
37302 * @param {Roo.bootstrap.SplitBar} this
37304 "beforeresize" : true,
37306 "beforeapply" : true
37309 Roo.util.Observable.call(this);
37312 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37313 onStartProxyDrag : function(x, y){
37314 this.fireEvent("beforeresize", this);
37316 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37318 o.enableDisplayMode("block");
37319 // all splitbars share the same overlay
37320 Roo.bootstrap.SplitBar.prototype.overlay = o;
37322 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37323 this.overlay.show();
37324 Roo.get(this.proxy).setDisplayed("block");
37325 var size = this.adapter.getElementSize(this);
37326 this.activeMinSize = this.getMinimumSize();;
37327 this.activeMaxSize = this.getMaximumSize();;
37328 var c1 = size - this.activeMinSize;
37329 var c2 = Math.max(this.activeMaxSize - size, 0);
37330 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37331 this.dd.resetConstraints();
37332 this.dd.setXConstraint(
37333 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37334 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37336 this.dd.setYConstraint(0, 0);
37338 this.dd.resetConstraints();
37339 this.dd.setXConstraint(0, 0);
37340 this.dd.setYConstraint(
37341 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37342 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37345 this.dragSpecs.startSize = size;
37346 this.dragSpecs.startPoint = [x, y];
37347 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37351 * @private Called after the drag operation by the DDProxy
37353 onEndProxyDrag : function(e){
37354 Roo.get(this.proxy).setDisplayed(false);
37355 var endPoint = Roo.lib.Event.getXY(e);
37357 this.overlay.hide();
37360 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37361 newSize = this.dragSpecs.startSize +
37362 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37363 endPoint[0] - this.dragSpecs.startPoint[0] :
37364 this.dragSpecs.startPoint[0] - endPoint[0]
37367 newSize = this.dragSpecs.startSize +
37368 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37369 endPoint[1] - this.dragSpecs.startPoint[1] :
37370 this.dragSpecs.startPoint[1] - endPoint[1]
37373 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37374 if(newSize != this.dragSpecs.startSize){
37375 if(this.fireEvent('beforeapply', this, newSize) !== false){
37376 this.adapter.setElementSize(this, newSize);
37377 this.fireEvent("moved", this, newSize);
37378 this.fireEvent("resize", this, newSize);
37384 * Get the adapter this SplitBar uses
37385 * @return The adapter object
37387 getAdapter : function(){
37388 return this.adapter;
37392 * Set the adapter this SplitBar uses
37393 * @param {Object} adapter A SplitBar adapter object
37395 setAdapter : function(adapter){
37396 this.adapter = adapter;
37397 this.adapter.init(this);
37401 * Gets the minimum size for the resizing element
37402 * @return {Number} The minimum size
37404 getMinimumSize : function(){
37405 return this.minSize;
37409 * Sets the minimum size for the resizing element
37410 * @param {Number} minSize The minimum size
37412 setMinimumSize : function(minSize){
37413 this.minSize = minSize;
37417 * Gets the maximum size for the resizing element
37418 * @return {Number} The maximum size
37420 getMaximumSize : function(){
37421 return this.maxSize;
37425 * Sets the maximum size for the resizing element
37426 * @param {Number} maxSize The maximum size
37428 setMaximumSize : function(maxSize){
37429 this.maxSize = maxSize;
37433 * Sets the initialize size for the resizing element
37434 * @param {Number} size The initial size
37436 setCurrentSize : function(size){
37437 var oldAnimate = this.animate;
37438 this.animate = false;
37439 this.adapter.setElementSize(this, size);
37440 this.animate = oldAnimate;
37444 * Destroy this splitbar.
37445 * @param {Boolean} removeEl True to remove the element
37447 destroy : function(removeEl){
37449 this.shim.remove();
37452 this.proxy.parentNode.removeChild(this.proxy);
37460 * @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.
37462 Roo.bootstrap.SplitBar.createProxy = function(dir){
37463 var proxy = new Roo.Element(document.createElement("div"));
37464 proxy.unselectable();
37465 var cls = 'roo-splitbar-proxy';
37466 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37467 document.body.appendChild(proxy.dom);
37472 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37473 * Default Adapter. It assumes the splitter and resizing element are not positioned
37474 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37476 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37479 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37480 // do nothing for now
37481 init : function(s){
37485 * Called before drag operations to get the current size of the resizing element.
37486 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37488 getElementSize : function(s){
37489 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37490 return s.resizingEl.getWidth();
37492 return s.resizingEl.getHeight();
37497 * Called after drag operations to set the size of the resizing element.
37498 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37499 * @param {Number} newSize The new size to set
37500 * @param {Function} onComplete A function to be invoked when resizing is complete
37502 setElementSize : function(s, newSize, onComplete){
37503 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37505 s.resizingEl.setWidth(newSize);
37507 onComplete(s, newSize);
37510 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37515 s.resizingEl.setHeight(newSize);
37517 onComplete(s, newSize);
37520 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37527 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37528 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37529 * Adapter that moves the splitter element to align with the resized sizing element.
37530 * Used with an absolute positioned SplitBar.
37531 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37532 * document.body, make sure you assign an id to the body element.
37534 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37535 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37536 this.container = Roo.get(container);
37539 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37540 init : function(s){
37541 this.basic.init(s);
37544 getElementSize : function(s){
37545 return this.basic.getElementSize(s);
37548 setElementSize : function(s, newSize, onComplete){
37549 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37552 moveSplitter : function(s){
37553 var yes = Roo.bootstrap.SplitBar;
37554 switch(s.placement){
37556 s.el.setX(s.resizingEl.getRight());
37559 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37562 s.el.setY(s.resizingEl.getBottom());
37565 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37572 * Orientation constant - Create a vertical SplitBar
37576 Roo.bootstrap.SplitBar.VERTICAL = 1;
37579 * Orientation constant - Create a horizontal SplitBar
37583 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37586 * Placement constant - The resizing element is to the left of the splitter element
37590 Roo.bootstrap.SplitBar.LEFT = 1;
37593 * Placement constant - The resizing element is to the right of the splitter element
37597 Roo.bootstrap.SplitBar.RIGHT = 2;
37600 * Placement constant - The resizing element is positioned above the splitter element
37604 Roo.bootstrap.SplitBar.TOP = 3;
37607 * Placement constant - The resizing element is positioned under splitter element
37611 Roo.bootstrap.SplitBar.BOTTOM = 4;
37612 Roo.namespace("Roo.bootstrap.layout");/*
37614 * Ext JS Library 1.1.1
37615 * Copyright(c) 2006-2007, Ext JS, LLC.
37617 * Originally Released Under LGPL - original licence link has changed is not relivant.
37620 * <script type="text/javascript">
37624 * @class Roo.bootstrap.layout.Manager
37625 * @extends Roo.bootstrap.Component
37626 * Base class for layout managers.
37628 Roo.bootstrap.layout.Manager = function(config)
37630 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37636 /** false to disable window resize monitoring @type Boolean */
37637 this.monitorWindowResize = true;
37642 * Fires when a layout is performed.
37643 * @param {Roo.LayoutManager} this
37647 * @event regionresized
37648 * Fires when the user resizes a region.
37649 * @param {Roo.LayoutRegion} region The resized region
37650 * @param {Number} newSize The new size (width for east/west, height for north/south)
37652 "regionresized" : true,
37654 * @event regioncollapsed
37655 * Fires when a region is collapsed.
37656 * @param {Roo.LayoutRegion} region The collapsed region
37658 "regioncollapsed" : true,
37660 * @event regionexpanded
37661 * Fires when a region is expanded.
37662 * @param {Roo.LayoutRegion} region The expanded region
37664 "regionexpanded" : true
37666 this.updating = false;
37669 this.el = Roo.get(config.el);
37675 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37680 monitorWindowResize : true,
37686 onRender : function(ct, position)
37689 this.el = Roo.get(ct);
37692 //this.fireEvent('render',this);
37696 initEvents: function()
37700 // ie scrollbar fix
37701 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37702 document.body.scroll = "no";
37703 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37704 this.el.position('relative');
37706 this.id = this.el.id;
37707 this.el.addClass("roo-layout-container");
37708 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37709 if(this.el.dom != document.body ) {
37710 this.el.on('resize', this.layout,this);
37711 this.el.on('show', this.layout,this);
37717 * Returns true if this layout is currently being updated
37718 * @return {Boolean}
37720 isUpdating : function(){
37721 return this.updating;
37725 * Suspend the LayoutManager from doing auto-layouts while
37726 * making multiple add or remove calls
37728 beginUpdate : function(){
37729 this.updating = true;
37733 * Restore auto-layouts and optionally disable the manager from performing a layout
37734 * @param {Boolean} noLayout true to disable a layout update
37736 endUpdate : function(noLayout){
37737 this.updating = false;
37743 layout: function(){
37747 onRegionResized : function(region, newSize){
37748 this.fireEvent("regionresized", region, newSize);
37752 onRegionCollapsed : function(region){
37753 this.fireEvent("regioncollapsed", region);
37756 onRegionExpanded : function(region){
37757 this.fireEvent("regionexpanded", region);
37761 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37762 * performs box-model adjustments.
37763 * @return {Object} The size as an object {width: (the width), height: (the height)}
37765 getViewSize : function()
37768 if(this.el.dom != document.body){
37769 size = this.el.getSize();
37771 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37773 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37774 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37779 * Returns the Element this layout is bound to.
37780 * @return {Roo.Element}
37782 getEl : function(){
37787 * Returns the specified region.
37788 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37789 * @return {Roo.LayoutRegion}
37791 getRegion : function(target){
37792 return this.regions[target.toLowerCase()];
37795 onWindowResize : function(){
37796 if(this.monitorWindowResize){
37803 * Ext JS Library 1.1.1
37804 * Copyright(c) 2006-2007, Ext JS, LLC.
37806 * Originally Released Under LGPL - original licence link has changed is not relivant.
37809 * <script type="text/javascript">
37812 * @class Roo.bootstrap.layout.Border
37813 * @extends Roo.bootstrap.layout.Manager
37814 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37815 * please see: examples/bootstrap/nested.html<br><br>
37817 <b>The container the layout is rendered into can be either the body element or any other element.
37818 If it is not the body element, the container needs to either be an absolute positioned element,
37819 or you will need to add "position:relative" to the css of the container. You will also need to specify
37820 the container size if it is not the body element.</b>
37823 * Create a new Border
37824 * @param {Object} config Configuration options
37826 Roo.bootstrap.layout.Border = function(config){
37827 config = config || {};
37828 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37832 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37833 if(config[region]){
37834 config[region].region = region;
37835 this.addRegion(config[region]);
37841 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37843 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37845 parent : false, // this might point to a 'nest' or a ???
37848 * Creates and adds a new region if it doesn't already exist.
37849 * @param {String} target The target region key (north, south, east, west or center).
37850 * @param {Object} config The regions config object
37851 * @return {BorderLayoutRegion} The new region
37853 addRegion : function(config)
37855 if(!this.regions[config.region]){
37856 var r = this.factory(config);
37857 this.bindRegion(r);
37859 return this.regions[config.region];
37863 bindRegion : function(r){
37864 this.regions[r.config.region] = r;
37866 r.on("visibilitychange", this.layout, this);
37867 r.on("paneladded", this.layout, this);
37868 r.on("panelremoved", this.layout, this);
37869 r.on("invalidated", this.layout, this);
37870 r.on("resized", this.onRegionResized, this);
37871 r.on("collapsed", this.onRegionCollapsed, this);
37872 r.on("expanded", this.onRegionExpanded, this);
37876 * Performs a layout update.
37878 layout : function()
37880 if(this.updating) {
37884 // render all the rebions if they have not been done alreayd?
37885 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37886 if(this.regions[region] && !this.regions[region].bodyEl){
37887 this.regions[region].onRender(this.el)
37891 var size = this.getViewSize();
37892 var w = size.width;
37893 var h = size.height;
37898 //var x = 0, y = 0;
37900 var rs = this.regions;
37901 var north = rs["north"];
37902 var south = rs["south"];
37903 var west = rs["west"];
37904 var east = rs["east"];
37905 var center = rs["center"];
37906 //if(this.hideOnLayout){ // not supported anymore
37907 //c.el.setStyle("display", "none");
37909 if(north && north.isVisible()){
37910 var b = north.getBox();
37911 var m = north.getMargins();
37912 b.width = w - (m.left+m.right);
37915 centerY = b.height + b.y + m.bottom;
37916 centerH -= centerY;
37917 north.updateBox(this.safeBox(b));
37919 if(south && south.isVisible()){
37920 var b = south.getBox();
37921 var m = south.getMargins();
37922 b.width = w - (m.left+m.right);
37924 var totalHeight = (b.height + m.top + m.bottom);
37925 b.y = h - totalHeight + m.top;
37926 centerH -= totalHeight;
37927 south.updateBox(this.safeBox(b));
37929 if(west && west.isVisible()){
37930 var b = west.getBox();
37931 var m = west.getMargins();
37932 b.height = centerH - (m.top+m.bottom);
37934 b.y = centerY + m.top;
37935 var totalWidth = (b.width + m.left + m.right);
37936 centerX += totalWidth;
37937 centerW -= totalWidth;
37938 west.updateBox(this.safeBox(b));
37940 if(east && east.isVisible()){
37941 var b = east.getBox();
37942 var m = east.getMargins();
37943 b.height = centerH - (m.top+m.bottom);
37944 var totalWidth = (b.width + m.left + m.right);
37945 b.x = w - totalWidth + m.left;
37946 b.y = centerY + m.top;
37947 centerW -= totalWidth;
37948 east.updateBox(this.safeBox(b));
37951 var m = center.getMargins();
37953 x: centerX + m.left,
37954 y: centerY + m.top,
37955 width: centerW - (m.left+m.right),
37956 height: centerH - (m.top+m.bottom)
37958 //if(this.hideOnLayout){
37959 //center.el.setStyle("display", "block");
37961 center.updateBox(this.safeBox(centerBox));
37964 this.fireEvent("layout", this);
37968 safeBox : function(box){
37969 box.width = Math.max(0, box.width);
37970 box.height = Math.max(0, box.height);
37975 * Adds a ContentPanel (or subclass) to this layout.
37976 * @param {String} target The target region key (north, south, east, west or center).
37977 * @param {Roo.ContentPanel} panel The panel to add
37978 * @return {Roo.ContentPanel} The added panel
37980 add : function(target, panel){
37982 target = target.toLowerCase();
37983 return this.regions[target].add(panel);
37987 * Remove a ContentPanel (or subclass) to this layout.
37988 * @param {String} target The target region key (north, south, east, west or center).
37989 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37990 * @return {Roo.ContentPanel} The removed panel
37992 remove : function(target, panel){
37993 target = target.toLowerCase();
37994 return this.regions[target].remove(panel);
37998 * Searches all regions for a panel with the specified id
37999 * @param {String} panelId
38000 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38002 findPanel : function(panelId){
38003 var rs = this.regions;
38004 for(var target in rs){
38005 if(typeof rs[target] != "function"){
38006 var p = rs[target].getPanel(panelId);
38016 * Searches all regions for a panel with the specified id and activates (shows) it.
38017 * @param {String/ContentPanel} panelId The panels id or the panel itself
38018 * @return {Roo.ContentPanel} The shown panel or null
38020 showPanel : function(panelId) {
38021 var rs = this.regions;
38022 for(var target in rs){
38023 var r = rs[target];
38024 if(typeof r != "function"){
38025 if(r.hasPanel(panelId)){
38026 return r.showPanel(panelId);
38034 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38035 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38038 restoreState : function(provider){
38040 provider = Roo.state.Manager;
38042 var sm = new Roo.LayoutStateManager();
38043 sm.init(this, provider);
38049 * Adds a xtype elements to the layout.
38053 xtype : 'ContentPanel',
38060 xtype : 'NestedLayoutPanel',
38066 items : [ ... list of content panels or nested layout panels.. ]
38070 * @param {Object} cfg Xtype definition of item to add.
38072 addxtype : function(cfg)
38074 // basically accepts a pannel...
38075 // can accept a layout region..!?!?
38076 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38079 // theory? children can only be panels??
38081 //if (!cfg.xtype.match(/Panel$/)) {
38086 if (typeof(cfg.region) == 'undefined') {
38087 Roo.log("Failed to add Panel, region was not set");
38091 var region = cfg.region;
38097 xitems = cfg.items;
38102 if ( region == 'center') {
38103 Roo.log("Center: " + cfg.title);
38109 case 'Content': // ContentPanel (el, cfg)
38110 case 'Scroll': // ContentPanel (el, cfg)
38112 cfg.autoCreate = cfg.autoCreate || true;
38113 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38115 // var el = this.el.createChild();
38116 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38119 this.add(region, ret);
38123 case 'TreePanel': // our new panel!
38124 cfg.el = this.el.createChild();
38125 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38126 this.add(region, ret);
38131 // create a new Layout (which is a Border Layout...
38133 var clayout = cfg.layout;
38134 clayout.el = this.el.createChild();
38135 clayout.items = clayout.items || [];
38139 // replace this exitems with the clayout ones..
38140 xitems = clayout.items;
38142 // force background off if it's in center...
38143 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38144 cfg.background = false;
38146 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38149 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38150 //console.log('adding nested layout panel ' + cfg.toSource());
38151 this.add(region, ret);
38152 nb = {}; /// find first...
38157 // needs grid and region
38159 //var el = this.getRegion(region).el.createChild();
38161 *var el = this.el.createChild();
38162 // create the grid first...
38163 cfg.grid.container = el;
38164 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38167 if (region == 'center' && this.active ) {
38168 cfg.background = false;
38171 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38173 this.add(region, ret);
38175 if (cfg.background) {
38176 // render grid on panel activation (if panel background)
38177 ret.on('activate', function(gp) {
38178 if (!gp.grid.rendered) {
38179 // gp.grid.render(el);
38183 // cfg.grid.render(el);
38189 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38190 // it was the old xcomponent building that caused this before.
38191 // espeically if border is the top element in the tree.
38201 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38203 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38204 this.add(region, ret);
38208 throw "Can not add '" + cfg.xtype + "' to Border";
38214 this.beginUpdate();
38218 Roo.each(xitems, function(i) {
38219 region = nb && i.region ? i.region : false;
38221 var add = ret.addxtype(i);
38224 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38225 if (!i.background) {
38226 abn[region] = nb[region] ;
38233 // make the last non-background panel active..
38234 //if (nb) { Roo.log(abn); }
38237 for(var r in abn) {
38238 region = this.getRegion(r);
38240 // tried using nb[r], but it does not work..
38242 region.showPanel(abn[r]);
38253 factory : function(cfg)
38256 var validRegions = Roo.bootstrap.layout.Border.regions;
38258 var target = cfg.region;
38261 var r = Roo.bootstrap.layout;
38265 return new r.North(cfg);
38267 return new r.South(cfg);
38269 return new r.East(cfg);
38271 return new r.West(cfg);
38273 return new r.Center(cfg);
38275 throw 'Layout region "'+target+'" not supported.';
38282 * Ext JS Library 1.1.1
38283 * Copyright(c) 2006-2007, Ext JS, LLC.
38285 * Originally Released Under LGPL - original licence link has changed is not relivant.
38288 * <script type="text/javascript">
38292 * @class Roo.bootstrap.layout.Basic
38293 * @extends Roo.util.Observable
38294 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38295 * and does not have a titlebar, tabs or any other features. All it does is size and position
38296 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38297 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38298 * @cfg {string} region the region that it inhabits..
38299 * @cfg {bool} skipConfig skip config?
38303 Roo.bootstrap.layout.Basic = function(config){
38305 this.mgr = config.mgr;
38307 this.position = config.region;
38309 var skipConfig = config.skipConfig;
38313 * @scope Roo.BasicLayoutRegion
38317 * @event beforeremove
38318 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38319 * @param {Roo.LayoutRegion} this
38320 * @param {Roo.ContentPanel} panel The panel
38321 * @param {Object} e The cancel event object
38323 "beforeremove" : true,
38325 * @event invalidated
38326 * Fires when the layout for this region is changed.
38327 * @param {Roo.LayoutRegion} this
38329 "invalidated" : true,
38331 * @event visibilitychange
38332 * Fires when this region is shown or hidden
38333 * @param {Roo.LayoutRegion} this
38334 * @param {Boolean} visibility true or false
38336 "visibilitychange" : true,
38338 * @event paneladded
38339 * Fires when a panel is added.
38340 * @param {Roo.LayoutRegion} this
38341 * @param {Roo.ContentPanel} panel The panel
38343 "paneladded" : true,
38345 * @event panelremoved
38346 * Fires when a panel is removed.
38347 * @param {Roo.LayoutRegion} this
38348 * @param {Roo.ContentPanel} panel The panel
38350 "panelremoved" : true,
38352 * @event beforecollapse
38353 * Fires when this region before collapse.
38354 * @param {Roo.LayoutRegion} this
38356 "beforecollapse" : true,
38359 * Fires when this region is collapsed.
38360 * @param {Roo.LayoutRegion} this
38362 "collapsed" : true,
38365 * Fires when this region is expanded.
38366 * @param {Roo.LayoutRegion} this
38371 * Fires when this region is slid into view.
38372 * @param {Roo.LayoutRegion} this
38374 "slideshow" : true,
38377 * Fires when this region slides out of view.
38378 * @param {Roo.LayoutRegion} this
38380 "slidehide" : true,
38382 * @event panelactivated
38383 * Fires when a panel is activated.
38384 * @param {Roo.LayoutRegion} this
38385 * @param {Roo.ContentPanel} panel The activated panel
38387 "panelactivated" : true,
38390 * Fires when the user resizes this region.
38391 * @param {Roo.LayoutRegion} this
38392 * @param {Number} newSize The new size (width for east/west, height for north/south)
38396 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38397 this.panels = new Roo.util.MixedCollection();
38398 this.panels.getKey = this.getPanelId.createDelegate(this);
38400 this.activePanel = null;
38401 // ensure listeners are added...
38403 if (config.listeners || config.events) {
38404 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38405 listeners : config.listeners || {},
38406 events : config.events || {}
38410 if(skipConfig !== true){
38411 this.applyConfig(config);
38415 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38417 getPanelId : function(p){
38421 applyConfig : function(config){
38422 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38423 this.config = config;
38428 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38429 * the width, for horizontal (north, south) the height.
38430 * @param {Number} newSize The new width or height
38432 resizeTo : function(newSize){
38433 var el = this.el ? this.el :
38434 (this.activePanel ? this.activePanel.getEl() : null);
38436 switch(this.position){
38439 el.setWidth(newSize);
38440 this.fireEvent("resized", this, newSize);
38444 el.setHeight(newSize);
38445 this.fireEvent("resized", this, newSize);
38451 getBox : function(){
38452 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38455 getMargins : function(){
38456 return this.margins;
38459 updateBox : function(box){
38461 var el = this.activePanel.getEl();
38462 el.dom.style.left = box.x + "px";
38463 el.dom.style.top = box.y + "px";
38464 this.activePanel.setSize(box.width, box.height);
38468 * Returns the container element for this region.
38469 * @return {Roo.Element}
38471 getEl : function(){
38472 return this.activePanel;
38476 * Returns true if this region is currently visible.
38477 * @return {Boolean}
38479 isVisible : function(){
38480 return this.activePanel ? true : false;
38483 setActivePanel : function(panel){
38484 panel = this.getPanel(panel);
38485 if(this.activePanel && this.activePanel != panel){
38486 this.activePanel.setActiveState(false);
38487 this.activePanel.getEl().setLeftTop(-10000,-10000);
38489 this.activePanel = panel;
38490 panel.setActiveState(true);
38492 panel.setSize(this.box.width, this.box.height);
38494 this.fireEvent("panelactivated", this, panel);
38495 this.fireEvent("invalidated");
38499 * Show the specified panel.
38500 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38501 * @return {Roo.ContentPanel} The shown panel or null
38503 showPanel : function(panel){
38504 panel = this.getPanel(panel);
38506 this.setActivePanel(panel);
38512 * Get the active panel for this region.
38513 * @return {Roo.ContentPanel} The active panel or null
38515 getActivePanel : function(){
38516 return this.activePanel;
38520 * Add the passed ContentPanel(s)
38521 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38522 * @return {Roo.ContentPanel} The panel added (if only one was added)
38524 add : function(panel){
38525 if(arguments.length > 1){
38526 for(var i = 0, len = arguments.length; i < len; i++) {
38527 this.add(arguments[i]);
38531 if(this.hasPanel(panel)){
38532 this.showPanel(panel);
38535 var el = panel.getEl();
38536 if(el.dom.parentNode != this.mgr.el.dom){
38537 this.mgr.el.dom.appendChild(el.dom);
38539 if(panel.setRegion){
38540 panel.setRegion(this);
38542 this.panels.add(panel);
38543 el.setStyle("position", "absolute");
38544 if(!panel.background){
38545 this.setActivePanel(panel);
38546 if(this.config.initialSize && this.panels.getCount()==1){
38547 this.resizeTo(this.config.initialSize);
38550 this.fireEvent("paneladded", this, panel);
38555 * Returns true if the panel is in this region.
38556 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38557 * @return {Boolean}
38559 hasPanel : function(panel){
38560 if(typeof panel == "object"){ // must be panel obj
38561 panel = panel.getId();
38563 return this.getPanel(panel) ? true : false;
38567 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38568 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38569 * @param {Boolean} preservePanel Overrides the config preservePanel option
38570 * @return {Roo.ContentPanel} The panel that was removed
38572 remove : function(panel, preservePanel){
38573 panel = this.getPanel(panel);
38578 this.fireEvent("beforeremove", this, panel, e);
38579 if(e.cancel === true){
38582 var panelId = panel.getId();
38583 this.panels.removeKey(panelId);
38588 * Returns the panel specified or null if it's not in this region.
38589 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38590 * @return {Roo.ContentPanel}
38592 getPanel : function(id){
38593 if(typeof id == "object"){ // must be panel obj
38596 return this.panels.get(id);
38600 * Returns this regions position (north/south/east/west/center).
38603 getPosition: function(){
38604 return this.position;
38608 * Ext JS Library 1.1.1
38609 * Copyright(c) 2006-2007, Ext JS, LLC.
38611 * Originally Released Under LGPL - original licence link has changed is not relivant.
38614 * <script type="text/javascript">
38618 * @class Roo.bootstrap.layout.Region
38619 * @extends Roo.bootstrap.layout.Basic
38620 * This class represents a region in a layout manager.
38622 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38623 * @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})
38624 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38625 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38626 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38627 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38628 * @cfg {String} title The title for the region (overrides panel titles)
38629 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38630 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38631 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38632 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38633 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38634 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38635 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38636 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38637 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38638 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38640 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38641 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38642 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38643 * @cfg {Number} width For East/West panels
38644 * @cfg {Number} height For North/South panels
38645 * @cfg {Boolean} split To show the splitter
38646 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38648 * @cfg {string} cls Extra CSS classes to add to region
38650 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38651 * @cfg {string} region the region that it inhabits..
38654 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38655 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38657 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38658 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38659 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38661 Roo.bootstrap.layout.Region = function(config)
38663 this.applyConfig(config);
38665 var mgr = config.mgr;
38666 var pos = config.region;
38667 config.skipConfig = true;
38668 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38671 this.onRender(mgr.el);
38674 this.visible = true;
38675 this.collapsed = false;
38676 this.unrendered_panels = [];
38679 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38681 position: '', // set by wrapper (eg. north/south etc..)
38682 unrendered_panels : null, // unrendered panels.
38684 tabPosition : false,
38686 mgr: false, // points to 'Border'
38689 createBody : function(){
38690 /** This region's body element
38691 * @type Roo.Element */
38692 this.bodyEl = this.el.createChild({
38694 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38698 onRender: function(ctr, pos)
38700 var dh = Roo.DomHelper;
38701 /** This region's container element
38702 * @type Roo.Element */
38703 this.el = dh.append(ctr.dom, {
38705 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38707 /** This region's title element
38708 * @type Roo.Element */
38710 this.titleEl = dh.append(this.el.dom, {
38712 unselectable: "on",
38713 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38715 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38716 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38720 this.titleEl.enableDisplayMode();
38721 /** This region's title text element
38722 * @type HTMLElement */
38723 this.titleTextEl = this.titleEl.dom.firstChild;
38724 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38726 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38727 this.closeBtn.enableDisplayMode();
38728 this.closeBtn.on("click", this.closeClicked, this);
38729 this.closeBtn.hide();
38731 this.createBody(this.config);
38732 if(this.config.hideWhenEmpty){
38734 this.on("paneladded", this.validateVisibility, this);
38735 this.on("panelremoved", this.validateVisibility, this);
38737 if(this.autoScroll){
38738 this.bodyEl.setStyle("overflow", "auto");
38740 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38742 //if(c.titlebar !== false){
38743 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38744 this.titleEl.hide();
38746 this.titleEl.show();
38747 if(this.config.title){
38748 this.titleTextEl.innerHTML = this.config.title;
38752 if(this.config.collapsed){
38753 this.collapse(true);
38755 if(this.config.hidden){
38759 if (this.unrendered_panels && this.unrendered_panels.length) {
38760 for (var i =0;i< this.unrendered_panels.length; i++) {
38761 this.add(this.unrendered_panels[i]);
38763 this.unrendered_panels = null;
38769 applyConfig : function(c)
38772 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38773 var dh = Roo.DomHelper;
38774 if(c.titlebar !== false){
38775 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38776 this.collapseBtn.on("click", this.collapse, this);
38777 this.collapseBtn.enableDisplayMode();
38779 if(c.showPin === true || this.showPin){
38780 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38781 this.stickBtn.enableDisplayMode();
38782 this.stickBtn.on("click", this.expand, this);
38783 this.stickBtn.hide();
38788 /** This region's collapsed element
38789 * @type Roo.Element */
38792 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38793 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38796 if(c.floatable !== false){
38797 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38798 this.collapsedEl.on("click", this.collapseClick, this);
38801 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38802 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38803 id: "message", unselectable: "on", style:{"float":"left"}});
38804 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38806 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38807 this.expandBtn.on("click", this.expand, this);
38811 if(this.collapseBtn){
38812 this.collapseBtn.setVisible(c.collapsible == true);
38815 this.cmargins = c.cmargins || this.cmargins ||
38816 (this.position == "west" || this.position == "east" ?
38817 {top: 0, left: 2, right:2, bottom: 0} :
38818 {top: 2, left: 0, right:0, bottom: 2});
38820 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38823 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38825 this.autoScroll = c.autoScroll || false;
38830 this.duration = c.duration || .30;
38831 this.slideDuration = c.slideDuration || .45;
38836 * Returns true if this region is currently visible.
38837 * @return {Boolean}
38839 isVisible : function(){
38840 return this.visible;
38844 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38845 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38847 //setCollapsedTitle : function(title){
38848 // title = title || " ";
38849 // if(this.collapsedTitleTextEl){
38850 // this.collapsedTitleTextEl.innerHTML = title;
38854 getBox : function(){
38856 // if(!this.collapsed){
38857 b = this.el.getBox(false, true);
38859 // b = this.collapsedEl.getBox(false, true);
38864 getMargins : function(){
38865 return this.margins;
38866 //return this.collapsed ? this.cmargins : this.margins;
38869 highlight : function(){
38870 this.el.addClass("x-layout-panel-dragover");
38873 unhighlight : function(){
38874 this.el.removeClass("x-layout-panel-dragover");
38877 updateBox : function(box)
38879 if (!this.bodyEl) {
38880 return; // not rendered yet..
38884 if(!this.collapsed){
38885 this.el.dom.style.left = box.x + "px";
38886 this.el.dom.style.top = box.y + "px";
38887 this.updateBody(box.width, box.height);
38889 this.collapsedEl.dom.style.left = box.x + "px";
38890 this.collapsedEl.dom.style.top = box.y + "px";
38891 this.collapsedEl.setSize(box.width, box.height);
38894 this.tabs.autoSizeTabs();
38898 updateBody : function(w, h)
38901 this.el.setWidth(w);
38902 w -= this.el.getBorderWidth("rl");
38903 if(this.config.adjustments){
38904 w += this.config.adjustments[0];
38907 if(h !== null && h > 0){
38908 this.el.setHeight(h);
38909 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38910 h -= this.el.getBorderWidth("tb");
38911 if(this.config.adjustments){
38912 h += this.config.adjustments[1];
38914 this.bodyEl.setHeight(h);
38916 h = this.tabs.syncHeight(h);
38919 if(this.panelSize){
38920 w = w !== null ? w : this.panelSize.width;
38921 h = h !== null ? h : this.panelSize.height;
38923 if(this.activePanel){
38924 var el = this.activePanel.getEl();
38925 w = w !== null ? w : el.getWidth();
38926 h = h !== null ? h : el.getHeight();
38927 this.panelSize = {width: w, height: h};
38928 this.activePanel.setSize(w, h);
38930 if(Roo.isIE && this.tabs){
38931 this.tabs.el.repaint();
38936 * Returns the container element for this region.
38937 * @return {Roo.Element}
38939 getEl : function(){
38944 * Hides this region.
38947 //if(!this.collapsed){
38948 this.el.dom.style.left = "-2000px";
38951 // this.collapsedEl.dom.style.left = "-2000px";
38952 // this.collapsedEl.hide();
38954 this.visible = false;
38955 this.fireEvent("visibilitychange", this, false);
38959 * Shows this region if it was previously hidden.
38962 //if(!this.collapsed){
38965 // this.collapsedEl.show();
38967 this.visible = true;
38968 this.fireEvent("visibilitychange", this, true);
38971 closeClicked : function(){
38972 if(this.activePanel){
38973 this.remove(this.activePanel);
38977 collapseClick : function(e){
38979 e.stopPropagation();
38982 e.stopPropagation();
38988 * Collapses this region.
38989 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38992 collapse : function(skipAnim, skipCheck = false){
38993 if(this.collapsed) {
38997 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38999 this.collapsed = true;
39001 this.split.el.hide();
39003 if(this.config.animate && skipAnim !== true){
39004 this.fireEvent("invalidated", this);
39005 this.animateCollapse();
39007 this.el.setLocation(-20000,-20000);
39009 this.collapsedEl.show();
39010 this.fireEvent("collapsed", this);
39011 this.fireEvent("invalidated", this);
39017 animateCollapse : function(){
39022 * Expands this region if it was previously collapsed.
39023 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39024 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39027 expand : function(e, skipAnim){
39029 e.stopPropagation();
39031 if(!this.collapsed || this.el.hasActiveFx()) {
39035 this.afterSlideIn();
39038 this.collapsed = false;
39039 if(this.config.animate && skipAnim !== true){
39040 this.animateExpand();
39044 this.split.el.show();
39046 this.collapsedEl.setLocation(-2000,-2000);
39047 this.collapsedEl.hide();
39048 this.fireEvent("invalidated", this);
39049 this.fireEvent("expanded", this);
39053 animateExpand : function(){
39057 initTabs : function()
39059 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39061 var ts = new Roo.bootstrap.panel.Tabs({
39062 el: this.bodyEl.dom,
39064 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39065 disableTooltips: this.config.disableTabTips,
39066 toolbar : this.config.toolbar
39069 if(this.config.hideTabs){
39070 ts.stripWrap.setDisplayed(false);
39073 ts.resizeTabs = this.config.resizeTabs === true;
39074 ts.minTabWidth = this.config.minTabWidth || 40;
39075 ts.maxTabWidth = this.config.maxTabWidth || 250;
39076 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39077 ts.monitorResize = false;
39078 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39079 ts.bodyEl.addClass('roo-layout-tabs-body');
39080 this.panels.each(this.initPanelAsTab, this);
39083 initPanelAsTab : function(panel){
39084 var ti = this.tabs.addTab(
39088 this.config.closeOnTab && panel.isClosable(),
39091 if(panel.tabTip !== undefined){
39092 ti.setTooltip(panel.tabTip);
39094 ti.on("activate", function(){
39095 this.setActivePanel(panel);
39098 if(this.config.closeOnTab){
39099 ti.on("beforeclose", function(t, e){
39101 this.remove(panel);
39105 panel.tabItem = ti;
39110 updatePanelTitle : function(panel, title)
39112 if(this.activePanel == panel){
39113 this.updateTitle(title);
39116 var ti = this.tabs.getTab(panel.getEl().id);
39118 if(panel.tabTip !== undefined){
39119 ti.setTooltip(panel.tabTip);
39124 updateTitle : function(title){
39125 if(this.titleTextEl && !this.config.title){
39126 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39130 setActivePanel : function(panel)
39132 panel = this.getPanel(panel);
39133 if(this.activePanel && this.activePanel != panel){
39134 if(this.activePanel.setActiveState(false) === false){
39138 this.activePanel = panel;
39139 panel.setActiveState(true);
39140 if(this.panelSize){
39141 panel.setSize(this.panelSize.width, this.panelSize.height);
39144 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39146 this.updateTitle(panel.getTitle());
39148 this.fireEvent("invalidated", this);
39150 this.fireEvent("panelactivated", this, panel);
39154 * Shows the specified panel.
39155 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39156 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39158 showPanel : function(panel)
39160 panel = this.getPanel(panel);
39163 var tab = this.tabs.getTab(panel.getEl().id);
39164 if(tab.isHidden()){
39165 this.tabs.unhideTab(tab.id);
39169 this.setActivePanel(panel);
39176 * Get the active panel for this region.
39177 * @return {Roo.ContentPanel} The active panel or null
39179 getActivePanel : function(){
39180 return this.activePanel;
39183 validateVisibility : function(){
39184 if(this.panels.getCount() < 1){
39185 this.updateTitle(" ");
39186 this.closeBtn.hide();
39189 if(!this.isVisible()){
39196 * Adds the passed ContentPanel(s) to this region.
39197 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39198 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39200 add : function(panel)
39202 if(arguments.length > 1){
39203 for(var i = 0, len = arguments.length; i < len; i++) {
39204 this.add(arguments[i]);
39209 // if we have not been rendered yet, then we can not really do much of this..
39210 if (!this.bodyEl) {
39211 this.unrendered_panels.push(panel);
39218 if(this.hasPanel(panel)){
39219 this.showPanel(panel);
39222 panel.setRegion(this);
39223 this.panels.add(panel);
39224 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39225 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39226 // and hide them... ???
39227 this.bodyEl.dom.appendChild(panel.getEl().dom);
39228 if(panel.background !== true){
39229 this.setActivePanel(panel);
39231 this.fireEvent("paneladded", this, panel);
39238 this.initPanelAsTab(panel);
39242 if(panel.background !== true){
39243 this.tabs.activate(panel.getEl().id);
39245 this.fireEvent("paneladded", this, panel);
39250 * Hides the tab for the specified panel.
39251 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39253 hidePanel : function(panel){
39254 if(this.tabs && (panel = this.getPanel(panel))){
39255 this.tabs.hideTab(panel.getEl().id);
39260 * Unhides the tab for a previously hidden panel.
39261 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39263 unhidePanel : function(panel){
39264 if(this.tabs && (panel = this.getPanel(panel))){
39265 this.tabs.unhideTab(panel.getEl().id);
39269 clearPanels : function(){
39270 while(this.panels.getCount() > 0){
39271 this.remove(this.panels.first());
39276 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39277 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39278 * @param {Boolean} preservePanel Overrides the config preservePanel option
39279 * @return {Roo.ContentPanel} The panel that was removed
39281 remove : function(panel, preservePanel)
39283 panel = this.getPanel(panel);
39288 this.fireEvent("beforeremove", this, panel, e);
39289 if(e.cancel === true){
39292 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39293 var panelId = panel.getId();
39294 this.panels.removeKey(panelId);
39296 document.body.appendChild(panel.getEl().dom);
39299 this.tabs.removeTab(panel.getEl().id);
39300 }else if (!preservePanel){
39301 this.bodyEl.dom.removeChild(panel.getEl().dom);
39303 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39304 var p = this.panels.first();
39305 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39306 tempEl.appendChild(p.getEl().dom);
39307 this.bodyEl.update("");
39308 this.bodyEl.dom.appendChild(p.getEl().dom);
39310 this.updateTitle(p.getTitle());
39312 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39313 this.setActivePanel(p);
39315 panel.setRegion(null);
39316 if(this.activePanel == panel){
39317 this.activePanel = null;
39319 if(this.config.autoDestroy !== false && preservePanel !== true){
39320 try{panel.destroy();}catch(e){}
39322 this.fireEvent("panelremoved", this, panel);
39327 * Returns the TabPanel component used by this region
39328 * @return {Roo.TabPanel}
39330 getTabs : function(){
39334 createTool : function(parentEl, className){
39335 var btn = Roo.DomHelper.append(parentEl, {
39337 cls: "x-layout-tools-button",
39340 cls: "roo-layout-tools-button-inner " + className,
39344 btn.addClassOnOver("roo-layout-tools-button-over");
39349 * Ext JS Library 1.1.1
39350 * Copyright(c) 2006-2007, Ext JS, LLC.
39352 * Originally Released Under LGPL - original licence link has changed is not relivant.
39355 * <script type="text/javascript">
39361 * @class Roo.SplitLayoutRegion
39362 * @extends Roo.LayoutRegion
39363 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39365 Roo.bootstrap.layout.Split = function(config){
39366 this.cursor = config.cursor;
39367 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39370 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39372 splitTip : "Drag to resize.",
39373 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39374 useSplitTips : false,
39376 applyConfig : function(config){
39377 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39380 onRender : function(ctr,pos) {
39382 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39383 if(!this.config.split){
39388 var splitEl = Roo.DomHelper.append(ctr.dom, {
39390 id: this.el.id + "-split",
39391 cls: "roo-layout-split roo-layout-split-"+this.position,
39394 /** The SplitBar for this region
39395 * @type Roo.SplitBar */
39396 // does not exist yet...
39397 Roo.log([this.position, this.orientation]);
39399 this.split = new Roo.bootstrap.SplitBar({
39400 dragElement : splitEl,
39401 resizingElement: this.el,
39402 orientation : this.orientation
39405 this.split.on("moved", this.onSplitMove, this);
39406 this.split.useShim = this.config.useShim === true;
39407 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39408 if(this.useSplitTips){
39409 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39411 //if(config.collapsible){
39412 // this.split.el.on("dblclick", this.collapse, this);
39415 if(typeof this.config.minSize != "undefined"){
39416 this.split.minSize = this.config.minSize;
39418 if(typeof this.config.maxSize != "undefined"){
39419 this.split.maxSize = this.config.maxSize;
39421 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39422 this.hideSplitter();
39427 getHMaxSize : function(){
39428 var cmax = this.config.maxSize || 10000;
39429 var center = this.mgr.getRegion("center");
39430 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39433 getVMaxSize : function(){
39434 var cmax = this.config.maxSize || 10000;
39435 var center = this.mgr.getRegion("center");
39436 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39439 onSplitMove : function(split, newSize){
39440 this.fireEvent("resized", this, newSize);
39444 * Returns the {@link Roo.SplitBar} for this region.
39445 * @return {Roo.SplitBar}
39447 getSplitBar : function(){
39452 this.hideSplitter();
39453 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39456 hideSplitter : function(){
39458 this.split.el.setLocation(-2000,-2000);
39459 this.split.el.hide();
39465 this.split.el.show();
39467 Roo.bootstrap.layout.Split.superclass.show.call(this);
39470 beforeSlide: function(){
39471 if(Roo.isGecko){// firefox overflow auto bug workaround
39472 this.bodyEl.clip();
39474 this.tabs.bodyEl.clip();
39476 if(this.activePanel){
39477 this.activePanel.getEl().clip();
39479 if(this.activePanel.beforeSlide){
39480 this.activePanel.beforeSlide();
39486 afterSlide : function(){
39487 if(Roo.isGecko){// firefox overflow auto bug workaround
39488 this.bodyEl.unclip();
39490 this.tabs.bodyEl.unclip();
39492 if(this.activePanel){
39493 this.activePanel.getEl().unclip();
39494 if(this.activePanel.afterSlide){
39495 this.activePanel.afterSlide();
39501 initAutoHide : function(){
39502 if(this.autoHide !== false){
39503 if(!this.autoHideHd){
39504 var st = new Roo.util.DelayedTask(this.slideIn, this);
39505 this.autoHideHd = {
39506 "mouseout": function(e){
39507 if(!e.within(this.el, true)){
39511 "mouseover" : function(e){
39517 this.el.on(this.autoHideHd);
39521 clearAutoHide : function(){
39522 if(this.autoHide !== false){
39523 this.el.un("mouseout", this.autoHideHd.mouseout);
39524 this.el.un("mouseover", this.autoHideHd.mouseover);
39528 clearMonitor : function(){
39529 Roo.get(document).un("click", this.slideInIf, this);
39532 // these names are backwards but not changed for compat
39533 slideOut : function(){
39534 if(this.isSlid || this.el.hasActiveFx()){
39537 this.isSlid = true;
39538 if(this.collapseBtn){
39539 this.collapseBtn.hide();
39541 this.closeBtnState = this.closeBtn.getStyle('display');
39542 this.closeBtn.hide();
39544 this.stickBtn.show();
39547 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39548 this.beforeSlide();
39549 this.el.setStyle("z-index", 10001);
39550 this.el.slideIn(this.getSlideAnchor(), {
39551 callback: function(){
39553 this.initAutoHide();
39554 Roo.get(document).on("click", this.slideInIf, this);
39555 this.fireEvent("slideshow", this);
39562 afterSlideIn : function(){
39563 this.clearAutoHide();
39564 this.isSlid = false;
39565 this.clearMonitor();
39566 this.el.setStyle("z-index", "");
39567 if(this.collapseBtn){
39568 this.collapseBtn.show();
39570 this.closeBtn.setStyle('display', this.closeBtnState);
39572 this.stickBtn.hide();
39574 this.fireEvent("slidehide", this);
39577 slideIn : function(cb){
39578 if(!this.isSlid || this.el.hasActiveFx()){
39582 this.isSlid = false;
39583 this.beforeSlide();
39584 this.el.slideOut(this.getSlideAnchor(), {
39585 callback: function(){
39586 this.el.setLeftTop(-10000, -10000);
39588 this.afterSlideIn();
39596 slideInIf : function(e){
39597 if(!e.within(this.el)){
39602 animateCollapse : function(){
39603 this.beforeSlide();
39604 this.el.setStyle("z-index", 20000);
39605 var anchor = this.getSlideAnchor();
39606 this.el.slideOut(anchor, {
39607 callback : function(){
39608 this.el.setStyle("z-index", "");
39609 this.collapsedEl.slideIn(anchor, {duration:.3});
39611 this.el.setLocation(-10000,-10000);
39613 this.fireEvent("collapsed", this);
39620 animateExpand : function(){
39621 this.beforeSlide();
39622 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39623 this.el.setStyle("z-index", 20000);
39624 this.collapsedEl.hide({
39627 this.el.slideIn(this.getSlideAnchor(), {
39628 callback : function(){
39629 this.el.setStyle("z-index", "");
39632 this.split.el.show();
39634 this.fireEvent("invalidated", this);
39635 this.fireEvent("expanded", this);
39663 getAnchor : function(){
39664 return this.anchors[this.position];
39667 getCollapseAnchor : function(){
39668 return this.canchors[this.position];
39671 getSlideAnchor : function(){
39672 return this.sanchors[this.position];
39675 getAlignAdj : function(){
39676 var cm = this.cmargins;
39677 switch(this.position){
39693 getExpandAdj : function(){
39694 var c = this.collapsedEl, cm = this.cmargins;
39695 switch(this.position){
39697 return [-(cm.right+c.getWidth()+cm.left), 0];
39700 return [cm.right+c.getWidth()+cm.left, 0];
39703 return [0, -(cm.top+cm.bottom+c.getHeight())];
39706 return [0, cm.top+cm.bottom+c.getHeight()];
39712 * Ext JS Library 1.1.1
39713 * Copyright(c) 2006-2007, Ext JS, LLC.
39715 * Originally Released Under LGPL - original licence link has changed is not relivant.
39718 * <script type="text/javascript">
39721 * These classes are private internal classes
39723 Roo.bootstrap.layout.Center = function(config){
39724 config.region = "center";
39725 Roo.bootstrap.layout.Region.call(this, config);
39726 this.visible = true;
39727 this.minWidth = config.minWidth || 20;
39728 this.minHeight = config.minHeight || 20;
39731 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39733 // center panel can't be hidden
39737 // center panel can't be hidden
39740 getMinWidth: function(){
39741 return this.minWidth;
39744 getMinHeight: function(){
39745 return this.minHeight;
39759 Roo.bootstrap.layout.North = function(config)
39761 config.region = 'north';
39762 config.cursor = 'n-resize';
39764 Roo.bootstrap.layout.Split.call(this, config);
39768 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39769 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39770 this.split.el.addClass("roo-layout-split-v");
39772 //var size = config.initialSize || config.height;
39773 //if(this.el && typeof size != "undefined"){
39774 // this.el.setHeight(size);
39777 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39779 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39782 onRender : function(ctr, pos)
39784 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39785 var size = this.config.initialSize || this.config.height;
39786 if(this.el && typeof size != "undefined"){
39787 this.el.setHeight(size);
39792 getBox : function(){
39793 if(this.collapsed){
39794 return this.collapsedEl.getBox();
39796 var box = this.el.getBox();
39798 box.height += this.split.el.getHeight();
39803 updateBox : function(box){
39804 if(this.split && !this.collapsed){
39805 box.height -= this.split.el.getHeight();
39806 this.split.el.setLeft(box.x);
39807 this.split.el.setTop(box.y+box.height);
39808 this.split.el.setWidth(box.width);
39810 if(this.collapsed){
39811 this.updateBody(box.width, null);
39813 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39821 Roo.bootstrap.layout.South = function(config){
39822 config.region = 'south';
39823 config.cursor = 's-resize';
39824 Roo.bootstrap.layout.Split.call(this, config);
39826 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39827 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39828 this.split.el.addClass("roo-layout-split-v");
39833 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39834 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39836 onRender : function(ctr, pos)
39838 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39839 var size = this.config.initialSize || this.config.height;
39840 if(this.el && typeof size != "undefined"){
39841 this.el.setHeight(size);
39846 getBox : function(){
39847 if(this.collapsed){
39848 return this.collapsedEl.getBox();
39850 var box = this.el.getBox();
39852 var sh = this.split.el.getHeight();
39859 updateBox : function(box){
39860 if(this.split && !this.collapsed){
39861 var sh = this.split.el.getHeight();
39864 this.split.el.setLeft(box.x);
39865 this.split.el.setTop(box.y-sh);
39866 this.split.el.setWidth(box.width);
39868 if(this.collapsed){
39869 this.updateBody(box.width, null);
39871 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39875 Roo.bootstrap.layout.East = function(config){
39876 config.region = "east";
39877 config.cursor = "e-resize";
39878 Roo.bootstrap.layout.Split.call(this, config);
39880 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39881 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39882 this.split.el.addClass("roo-layout-split-h");
39886 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39887 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39889 onRender : function(ctr, pos)
39891 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39892 var size = this.config.initialSize || this.config.width;
39893 if(this.el && typeof size != "undefined"){
39894 this.el.setWidth(size);
39899 getBox : function(){
39900 if(this.collapsed){
39901 return this.collapsedEl.getBox();
39903 var box = this.el.getBox();
39905 var sw = this.split.el.getWidth();
39912 updateBox : function(box){
39913 if(this.split && !this.collapsed){
39914 var sw = this.split.el.getWidth();
39916 this.split.el.setLeft(box.x);
39917 this.split.el.setTop(box.y);
39918 this.split.el.setHeight(box.height);
39921 if(this.collapsed){
39922 this.updateBody(null, box.height);
39924 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39928 Roo.bootstrap.layout.West = function(config){
39929 config.region = "west";
39930 config.cursor = "w-resize";
39932 Roo.bootstrap.layout.Split.call(this, config);
39934 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39935 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39936 this.split.el.addClass("roo-layout-split-h");
39940 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39941 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39943 onRender: function(ctr, pos)
39945 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39946 var size = this.config.initialSize || this.config.width;
39947 if(typeof size != "undefined"){
39948 this.el.setWidth(size);
39952 getBox : function(){
39953 if(this.collapsed){
39954 return this.collapsedEl.getBox();
39956 var box = this.el.getBox();
39957 if (box.width == 0) {
39958 box.width = this.config.width; // kludge?
39961 box.width += this.split.el.getWidth();
39966 updateBox : function(box){
39967 if(this.split && !this.collapsed){
39968 var sw = this.split.el.getWidth();
39970 this.split.el.setLeft(box.x+box.width);
39971 this.split.el.setTop(box.y);
39972 this.split.el.setHeight(box.height);
39974 if(this.collapsed){
39975 this.updateBody(null, box.height);
39977 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39979 });Roo.namespace("Roo.bootstrap.panel");/*
39981 * Ext JS Library 1.1.1
39982 * Copyright(c) 2006-2007, Ext JS, LLC.
39984 * Originally Released Under LGPL - original licence link has changed is not relivant.
39987 * <script type="text/javascript">
39990 * @class Roo.ContentPanel
39991 * @extends Roo.util.Observable
39992 * A basic ContentPanel element.
39993 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39994 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39995 * @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
39996 * @cfg {Boolean} closable True if the panel can be closed/removed
39997 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39998 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39999 * @cfg {Toolbar} toolbar A toolbar for this panel
40000 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40001 * @cfg {String} title The title for this panel
40002 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40003 * @cfg {String} url Calls {@link #setUrl} with this value
40004 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40005 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40006 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40007 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40008 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40009 * @cfg {Boolean} badges render the badges
40010 * @cfg {String} cls extra classes to use
40011 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40014 * Create a new ContentPanel.
40015 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40016 * @param {String/Object} config A string to set only the title or a config object
40017 * @param {String} content (optional) Set the HTML content for this panel
40018 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40020 Roo.bootstrap.panel.Content = function( config){
40022 this.tpl = config.tpl || false;
40024 var el = config.el;
40025 var content = config.content;
40027 if(config.autoCreate){ // xtype is available if this is called from factory
40030 this.el = Roo.get(el);
40031 if(!this.el && config && config.autoCreate){
40032 if(typeof config.autoCreate == "object"){
40033 if(!config.autoCreate.id){
40034 config.autoCreate.id = config.id||el;
40036 this.el = Roo.DomHelper.append(document.body,
40037 config.autoCreate, true);
40041 cls: (config.cls || '') +
40042 (config.background ? ' bg-' + config.background : '') +
40043 " roo-layout-inactive-content",
40046 if (config.iframe) {
40050 style : 'border: 0px',
40051 src : 'about:blank'
40057 elcfg.html = config.html;
40061 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40062 if (config.iframe) {
40063 this.iframeEl = this.el.select('iframe',true).first();
40068 this.closable = false;
40069 this.loaded = false;
40070 this.active = false;
40073 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40075 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40077 this.wrapEl = this.el; //this.el.wrap();
40079 if (config.toolbar.items) {
40080 ti = config.toolbar.items ;
40081 delete config.toolbar.items ;
40085 this.toolbar.render(this.wrapEl, 'before');
40086 for(var i =0;i < ti.length;i++) {
40087 // Roo.log(['add child', items[i]]);
40088 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40090 this.toolbar.items = nitems;
40091 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40092 delete config.toolbar;
40096 // xtype created footer. - not sure if will work as we normally have to render first..
40097 if (this.footer && !this.footer.el && this.footer.xtype) {
40098 if (!this.wrapEl) {
40099 this.wrapEl = this.el.wrap();
40102 this.footer.container = this.wrapEl.createChild();
40104 this.footer = Roo.factory(this.footer, Roo);
40109 if(typeof config == "string"){
40110 this.title = config;
40112 Roo.apply(this, config);
40116 this.resizeEl = Roo.get(this.resizeEl, true);
40118 this.resizeEl = this.el;
40120 // handle view.xtype
40128 * Fires when this panel is activated.
40129 * @param {Roo.ContentPanel} this
40133 * @event deactivate
40134 * Fires when this panel is activated.
40135 * @param {Roo.ContentPanel} this
40137 "deactivate" : true,
40141 * Fires when this panel is resized if fitToFrame is true.
40142 * @param {Roo.ContentPanel} this
40143 * @param {Number} width The width after any component adjustments
40144 * @param {Number} height The height after any component adjustments
40150 * Fires when this tab is created
40151 * @param {Roo.ContentPanel} this
40162 if(this.autoScroll && !this.iframe){
40163 this.resizeEl.setStyle("overflow", "auto");
40165 // fix randome scrolling
40166 //this.el.on('scroll', function() {
40167 // Roo.log('fix random scolling');
40168 // this.scrollTo('top',0);
40171 content = content || this.content;
40173 this.setContent(content);
40175 if(config && config.url){
40176 this.setUrl(this.url, this.params, this.loadOnce);
40181 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40183 if (this.view && typeof(this.view.xtype) != 'undefined') {
40184 this.view.el = this.el.appendChild(document.createElement("div"));
40185 this.view = Roo.factory(this.view);
40186 this.view.render && this.view.render(false, '');
40190 this.fireEvent('render', this);
40193 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40203 setRegion : function(region){
40204 this.region = region;
40205 this.setActiveClass(region && !this.background);
40209 setActiveClass: function(state)
40212 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40213 this.el.setStyle('position','relative');
40215 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40216 this.el.setStyle('position', 'absolute');
40221 * Returns the toolbar for this Panel if one was configured.
40222 * @return {Roo.Toolbar}
40224 getToolbar : function(){
40225 return this.toolbar;
40228 setActiveState : function(active)
40230 this.active = active;
40231 this.setActiveClass(active);
40233 if(this.fireEvent("deactivate", this) === false){
40238 this.fireEvent("activate", this);
40242 * Updates this panel's element (not for iframe)
40243 * @param {String} content The new content
40244 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40246 setContent : function(content, loadScripts){
40251 this.el.update(content, loadScripts);
40254 ignoreResize : function(w, h){
40255 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40258 this.lastSize = {width: w, height: h};
40263 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40264 * @return {Roo.UpdateManager} The UpdateManager
40266 getUpdateManager : function(){
40270 return this.el.getUpdateManager();
40273 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40274 * Does not work with IFRAME contents
40275 * @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:
40278 url: "your-url.php",
40279 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40280 callback: yourFunction,
40281 scope: yourObject, //(optional scope)
40284 text: "Loading...",
40290 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40291 * 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.
40292 * @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}
40293 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40294 * @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.
40295 * @return {Roo.ContentPanel} this
40303 var um = this.el.getUpdateManager();
40304 um.update.apply(um, arguments);
40310 * 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.
40311 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40312 * @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)
40313 * @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)
40314 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40316 setUrl : function(url, params, loadOnce){
40318 this.iframeEl.dom.src = url;
40322 if(this.refreshDelegate){
40323 this.removeListener("activate", this.refreshDelegate);
40325 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40326 this.on("activate", this.refreshDelegate);
40327 return this.el.getUpdateManager();
40330 _handleRefresh : function(url, params, loadOnce){
40331 if(!loadOnce || !this.loaded){
40332 var updater = this.el.getUpdateManager();
40333 updater.update(url, params, this._setLoaded.createDelegate(this));
40337 _setLoaded : function(){
40338 this.loaded = true;
40342 * Returns this panel's id
40345 getId : function(){
40350 * Returns this panel's element - used by regiosn to add.
40351 * @return {Roo.Element}
40353 getEl : function(){
40354 return this.wrapEl || this.el;
40359 adjustForComponents : function(width, height)
40361 //Roo.log('adjustForComponents ');
40362 if(this.resizeEl != this.el){
40363 width -= this.el.getFrameWidth('lr');
40364 height -= this.el.getFrameWidth('tb');
40367 var te = this.toolbar.getEl();
40368 te.setWidth(width);
40369 height -= te.getHeight();
40372 var te = this.footer.getEl();
40373 te.setWidth(width);
40374 height -= te.getHeight();
40378 if(this.adjustments){
40379 width += this.adjustments[0];
40380 height += this.adjustments[1];
40382 return {"width": width, "height": height};
40385 setSize : function(width, height){
40386 if(this.fitToFrame && !this.ignoreResize(width, height)){
40387 if(this.fitContainer && this.resizeEl != this.el){
40388 this.el.setSize(width, height);
40390 var size = this.adjustForComponents(width, height);
40392 this.iframeEl.setSize(width,height);
40395 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40396 this.fireEvent('resize', this, size.width, size.height);
40403 * Returns this panel's title
40406 getTitle : function(){
40408 if (typeof(this.title) != 'object') {
40413 for (var k in this.title) {
40414 if (!this.title.hasOwnProperty(k)) {
40418 if (k.indexOf('-') >= 0) {
40419 var s = k.split('-');
40420 for (var i = 0; i<s.length; i++) {
40421 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40424 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40431 * Set this panel's title
40432 * @param {String} title
40434 setTitle : function(title){
40435 this.title = title;
40437 this.region.updatePanelTitle(this, title);
40442 * Returns true is this panel was configured to be closable
40443 * @return {Boolean}
40445 isClosable : function(){
40446 return this.closable;
40449 beforeSlide : function(){
40451 this.resizeEl.clip();
40454 afterSlide : function(){
40456 this.resizeEl.unclip();
40460 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40461 * Will fail silently if the {@link #setUrl} method has not been called.
40462 * This does not activate the panel, just updates its content.
40464 refresh : function(){
40465 if(this.refreshDelegate){
40466 this.loaded = false;
40467 this.refreshDelegate();
40472 * Destroys this panel
40474 destroy : function(){
40475 this.el.removeAllListeners();
40476 var tempEl = document.createElement("span");
40477 tempEl.appendChild(this.el.dom);
40478 tempEl.innerHTML = "";
40484 * form - if the content panel contains a form - this is a reference to it.
40485 * @type {Roo.form.Form}
40489 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40490 * This contains a reference to it.
40496 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40506 * @param {Object} cfg Xtype definition of item to add.
40510 getChildContainer: function () {
40511 return this.getEl();
40516 var ret = new Roo.factory(cfg);
40521 if (cfg.xtype.match(/^Form$/)) {
40524 //if (this.footer) {
40525 // el = this.footer.container.insertSibling(false, 'before');
40527 el = this.el.createChild();
40530 this.form = new Roo.form.Form(cfg);
40533 if ( this.form.allItems.length) {
40534 this.form.render(el.dom);
40538 // should only have one of theses..
40539 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40540 // views.. should not be just added - used named prop 'view''
40542 cfg.el = this.el.appendChild(document.createElement("div"));
40545 var ret = new Roo.factory(cfg);
40547 ret.render && ret.render(false, ''); // render blank..
40557 * @class Roo.bootstrap.panel.Grid
40558 * @extends Roo.bootstrap.panel.Content
40560 * Create a new GridPanel.
40561 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40562 * @param {Object} config A the config object
40568 Roo.bootstrap.panel.Grid = function(config)
40572 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40573 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40575 config.el = this.wrapper;
40576 //this.el = this.wrapper;
40578 if (config.container) {
40579 // ctor'ed from a Border/panel.grid
40582 this.wrapper.setStyle("overflow", "hidden");
40583 this.wrapper.addClass('roo-grid-container');
40588 if(config.toolbar){
40589 var tool_el = this.wrapper.createChild();
40590 this.toolbar = Roo.factory(config.toolbar);
40592 if (config.toolbar.items) {
40593 ti = config.toolbar.items ;
40594 delete config.toolbar.items ;
40598 this.toolbar.render(tool_el);
40599 for(var i =0;i < ti.length;i++) {
40600 // Roo.log(['add child', items[i]]);
40601 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40603 this.toolbar.items = nitems;
40605 delete config.toolbar;
40608 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40609 config.grid.scrollBody = true;;
40610 config.grid.monitorWindowResize = false; // turn off autosizing
40611 config.grid.autoHeight = false;
40612 config.grid.autoWidth = false;
40614 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40616 if (config.background) {
40617 // render grid on panel activation (if panel background)
40618 this.on('activate', function(gp) {
40619 if (!gp.grid.rendered) {
40620 gp.grid.render(this.wrapper);
40621 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40626 this.grid.render(this.wrapper);
40627 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40630 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40631 // ??? needed ??? config.el = this.wrapper;
40636 // xtype created footer. - not sure if will work as we normally have to render first..
40637 if (this.footer && !this.footer.el && this.footer.xtype) {
40639 var ctr = this.grid.getView().getFooterPanel(true);
40640 this.footer.dataSource = this.grid.dataSource;
40641 this.footer = Roo.factory(this.footer, Roo);
40642 this.footer.render(ctr);
40652 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40653 getId : function(){
40654 return this.grid.id;
40658 * Returns the grid for this panel
40659 * @return {Roo.bootstrap.Table}
40661 getGrid : function(){
40665 setSize : function(width, height){
40666 if(!this.ignoreResize(width, height)){
40667 var grid = this.grid;
40668 var size = this.adjustForComponents(width, height);
40669 // tfoot is not a footer?
40672 var gridel = grid.getGridEl();
40673 gridel.setSize(size.width, size.height);
40675 var tbd = grid.getGridEl().select('tbody', true).first();
40676 var thd = grid.getGridEl().select('thead',true).first();
40677 var tbf= grid.getGridEl().select('tfoot', true).first();
40680 size.height -= tbf.getHeight();
40683 size.height -= thd.getHeight();
40686 tbd.setSize(size.width, size.height );
40687 // this is for the account management tab -seems to work there.
40688 var thd = grid.getGridEl().select('thead',true).first();
40690 // tbd.setSize(size.width, size.height - thd.getHeight());
40699 beforeSlide : function(){
40700 this.grid.getView().scroller.clip();
40703 afterSlide : function(){
40704 this.grid.getView().scroller.unclip();
40707 destroy : function(){
40708 this.grid.destroy();
40710 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40715 * @class Roo.bootstrap.panel.Nest
40716 * @extends Roo.bootstrap.panel.Content
40718 * Create a new Panel, that can contain a layout.Border.
40721 * @param {Roo.BorderLayout} layout The layout for this panel
40722 * @param {String/Object} config A string to set only the title or a config object
40724 Roo.bootstrap.panel.Nest = function(config)
40726 // construct with only one argument..
40727 /* FIXME - implement nicer consturctors
40728 if (layout.layout) {
40730 layout = config.layout;
40731 delete config.layout;
40733 if (layout.xtype && !layout.getEl) {
40734 // then layout needs constructing..
40735 layout = Roo.factory(layout, Roo);
40739 config.el = config.layout.getEl();
40741 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40743 config.layout.monitorWindowResize = false; // turn off autosizing
40744 this.layout = config.layout;
40745 this.layout.getEl().addClass("roo-layout-nested-layout");
40746 this.layout.parent = this;
40753 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40755 setSize : function(width, height){
40756 if(!this.ignoreResize(width, height)){
40757 var size = this.adjustForComponents(width, height);
40758 var el = this.layout.getEl();
40759 if (size.height < 1) {
40760 el.setWidth(size.width);
40762 el.setSize(size.width, size.height);
40764 var touch = el.dom.offsetWidth;
40765 this.layout.layout();
40766 // ie requires a double layout on the first pass
40767 if(Roo.isIE && !this.initialized){
40768 this.initialized = true;
40769 this.layout.layout();
40774 // activate all subpanels if not currently active..
40776 setActiveState : function(active){
40777 this.active = active;
40778 this.setActiveClass(active);
40781 this.fireEvent("deactivate", this);
40785 this.fireEvent("activate", this);
40786 // not sure if this should happen before or after..
40787 if (!this.layout) {
40788 return; // should not happen..
40791 for (var r in this.layout.regions) {
40792 reg = this.layout.getRegion(r);
40793 if (reg.getActivePanel()) {
40794 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40795 reg.setActivePanel(reg.getActivePanel());
40798 if (!reg.panels.length) {
40801 reg.showPanel(reg.getPanel(0));
40810 * Returns the nested BorderLayout for this panel
40811 * @return {Roo.BorderLayout}
40813 getLayout : function(){
40814 return this.layout;
40818 * Adds a xtype elements to the layout of the nested panel
40822 xtype : 'ContentPanel',
40829 xtype : 'NestedLayoutPanel',
40835 items : [ ... list of content panels or nested layout panels.. ]
40839 * @param {Object} cfg Xtype definition of item to add.
40841 addxtype : function(cfg) {
40842 return this.layout.addxtype(cfg);
40847 * Ext JS Library 1.1.1
40848 * Copyright(c) 2006-2007, Ext JS, LLC.
40850 * Originally Released Under LGPL - original licence link has changed is not relivant.
40853 * <script type="text/javascript">
40856 * @class Roo.TabPanel
40857 * @extends Roo.util.Observable
40858 * A lightweight tab container.
40862 // basic tabs 1, built from existing content
40863 var tabs = new Roo.TabPanel("tabs1");
40864 tabs.addTab("script", "View Script");
40865 tabs.addTab("markup", "View Markup");
40866 tabs.activate("script");
40868 // more advanced tabs, built from javascript
40869 var jtabs = new Roo.TabPanel("jtabs");
40870 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40872 // set up the UpdateManager
40873 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40874 var updater = tab2.getUpdateManager();
40875 updater.setDefaultUrl("ajax1.htm");
40876 tab2.on('activate', updater.refresh, updater, true);
40878 // Use setUrl for Ajax loading
40879 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40880 tab3.setUrl("ajax2.htm", null, true);
40883 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40886 jtabs.activate("jtabs-1");
40889 * Create a new TabPanel.
40890 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40891 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40893 Roo.bootstrap.panel.Tabs = function(config){
40895 * The container element for this TabPanel.
40896 * @type Roo.Element
40898 this.el = Roo.get(config.el);
40901 if(typeof config == "boolean"){
40902 this.tabPosition = config ? "bottom" : "top";
40904 Roo.apply(this, config);
40908 if(this.tabPosition == "bottom"){
40909 // if tabs are at the bottom = create the body first.
40910 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40911 this.el.addClass("roo-tabs-bottom");
40913 // next create the tabs holders
40915 if (this.tabPosition == "west"){
40917 var reg = this.region; // fake it..
40919 if (!reg.mgr.parent) {
40922 reg = reg.mgr.parent.region;
40924 Roo.log("got nest?");
40926 if (reg.mgr.getRegion('west')) {
40927 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40928 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40929 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40930 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40931 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40939 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40940 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40941 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40942 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40947 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40950 // finally - if tabs are at the top, then create the body last..
40951 if(this.tabPosition != "bottom"){
40952 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40953 * @type Roo.Element
40955 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40956 this.el.addClass("roo-tabs-top");
40960 this.bodyEl.setStyle("position", "relative");
40962 this.active = null;
40963 this.activateDelegate = this.activate.createDelegate(this);
40968 * Fires when the active tab changes
40969 * @param {Roo.TabPanel} this
40970 * @param {Roo.TabPanelItem} activePanel The new active tab
40974 * @event beforetabchange
40975 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40976 * @param {Roo.TabPanel} this
40977 * @param {Object} e Set cancel to true on this object to cancel the tab change
40978 * @param {Roo.TabPanelItem} tab The tab being changed to
40980 "beforetabchange" : true
40983 Roo.EventManager.onWindowResize(this.onResize, this);
40984 this.cpad = this.el.getPadding("lr");
40985 this.hiddenCount = 0;
40988 // toolbar on the tabbar support...
40989 if (this.toolbar) {
40990 alert("no toolbar support yet");
40991 this.toolbar = false;
40993 var tcfg = this.toolbar;
40994 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40995 this.toolbar = new Roo.Toolbar(tcfg);
40996 if (Roo.isSafari) {
40997 var tbl = tcfg.container.child('table', true);
40998 tbl.setAttribute('width', '100%');
41006 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41009 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41011 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41013 tabPosition : "top",
41015 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41017 currentTabWidth : 0,
41019 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41023 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41027 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41029 preferredTabWidth : 175,
41031 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41033 resizeTabs : false,
41035 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41037 monitorResize : true,
41039 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41041 toolbar : false, // set by caller..
41043 region : false, /// set by caller
41045 disableTooltips : true, // not used yet...
41048 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41049 * @param {String} id The id of the div to use <b>or create</b>
41050 * @param {String} text The text for the tab
41051 * @param {String} content (optional) Content to put in the TabPanelItem body
41052 * @param {Boolean} closable (optional) True to create a close icon on the tab
41053 * @return {Roo.TabPanelItem} The created TabPanelItem
41055 addTab : function(id, text, content, closable, tpl)
41057 var item = new Roo.bootstrap.panel.TabItem({
41061 closable : closable,
41064 this.addTabItem(item);
41066 item.setContent(content);
41072 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41073 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41074 * @return {Roo.TabPanelItem}
41076 getTab : function(id){
41077 return this.items[id];
41081 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41082 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41084 hideTab : function(id){
41085 var t = this.items[id];
41088 this.hiddenCount++;
41089 this.autoSizeTabs();
41094 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41095 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41097 unhideTab : function(id){
41098 var t = this.items[id];
41100 t.setHidden(false);
41101 this.hiddenCount--;
41102 this.autoSizeTabs();
41107 * Adds an existing {@link Roo.TabPanelItem}.
41108 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41110 addTabItem : function(item)
41112 this.items[item.id] = item;
41113 this.items.push(item);
41114 this.autoSizeTabs();
41115 // if(this.resizeTabs){
41116 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41117 // this.autoSizeTabs();
41119 // item.autoSize();
41124 * Removes a {@link Roo.TabPanelItem}.
41125 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41127 removeTab : function(id){
41128 var items = this.items;
41129 var tab = items[id];
41130 if(!tab) { return; }
41131 var index = items.indexOf(tab);
41132 if(this.active == tab && items.length > 1){
41133 var newTab = this.getNextAvailable(index);
41138 this.stripEl.dom.removeChild(tab.pnode.dom);
41139 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41140 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41142 items.splice(index, 1);
41143 delete this.items[tab.id];
41144 tab.fireEvent("close", tab);
41145 tab.purgeListeners();
41146 this.autoSizeTabs();
41149 getNextAvailable : function(start){
41150 var items = this.items;
41152 // look for a next tab that will slide over to
41153 // replace the one being removed
41154 while(index < items.length){
41155 var item = items[++index];
41156 if(item && !item.isHidden()){
41160 // if one isn't found select the previous tab (on the left)
41163 var item = items[--index];
41164 if(item && !item.isHidden()){
41172 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41173 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41175 disableTab : function(id){
41176 var tab = this.items[id];
41177 if(tab && this.active != tab){
41183 * Enables a {@link Roo.TabPanelItem} that is disabled.
41184 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41186 enableTab : function(id){
41187 var tab = this.items[id];
41192 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41193 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41194 * @return {Roo.TabPanelItem} The TabPanelItem.
41196 activate : function(id)
41198 //Roo.log('activite:' + id);
41200 var tab = this.items[id];
41204 if(tab == this.active || tab.disabled){
41208 this.fireEvent("beforetabchange", this, e, tab);
41209 if(e.cancel !== true && !tab.disabled){
41211 this.active.hide();
41213 this.active = this.items[id];
41214 this.active.show();
41215 this.fireEvent("tabchange", this, this.active);
41221 * Gets the active {@link Roo.TabPanelItem}.
41222 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41224 getActiveTab : function(){
41225 return this.active;
41229 * Updates the tab body element to fit the height of the container element
41230 * for overflow scrolling
41231 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41233 syncHeight : function(targetHeight){
41234 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41235 var bm = this.bodyEl.getMargins();
41236 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41237 this.bodyEl.setHeight(newHeight);
41241 onResize : function(){
41242 if(this.monitorResize){
41243 this.autoSizeTabs();
41248 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41250 beginUpdate : function(){
41251 this.updating = true;
41255 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41257 endUpdate : function(){
41258 this.updating = false;
41259 this.autoSizeTabs();
41263 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41265 autoSizeTabs : function()
41267 var count = this.items.length;
41268 var vcount = count - this.hiddenCount;
41271 this.stripEl.hide();
41273 this.stripEl.show();
41276 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41281 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41282 var availWidth = Math.floor(w / vcount);
41283 var b = this.stripBody;
41284 if(b.getWidth() > w){
41285 var tabs = this.items;
41286 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41287 if(availWidth < this.minTabWidth){
41288 /*if(!this.sleft){ // incomplete scrolling code
41289 this.createScrollButtons();
41292 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41295 if(this.currentTabWidth < this.preferredTabWidth){
41296 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41302 * Returns the number of tabs in this TabPanel.
41305 getCount : function(){
41306 return this.items.length;
41310 * Resizes all the tabs to the passed width
41311 * @param {Number} The new width
41313 setTabWidth : function(width){
41314 this.currentTabWidth = width;
41315 for(var i = 0, len = this.items.length; i < len; i++) {
41316 if(!this.items[i].isHidden()) {
41317 this.items[i].setWidth(width);
41323 * Destroys this TabPanel
41324 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41326 destroy : function(removeEl){
41327 Roo.EventManager.removeResizeListener(this.onResize, this);
41328 for(var i = 0, len = this.items.length; i < len; i++){
41329 this.items[i].purgeListeners();
41331 if(removeEl === true){
41332 this.el.update("");
41337 createStrip : function(container)
41339 var strip = document.createElement("nav");
41340 strip.className = Roo.bootstrap.version == 4 ?
41341 "navbar-light bg-light" :
41342 "navbar navbar-default"; //"x-tabs-wrap";
41343 container.appendChild(strip);
41347 createStripList : function(strip)
41349 // div wrapper for retard IE
41350 // returns the "tr" element.
41351 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41352 //'<div class="x-tabs-strip-wrap">'+
41353 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41354 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41355 return strip.firstChild; //.firstChild.firstChild.firstChild;
41357 createBody : function(container)
41359 var body = document.createElement("div");
41360 Roo.id(body, "tab-body");
41361 //Roo.fly(body).addClass("x-tabs-body");
41362 Roo.fly(body).addClass("tab-content");
41363 container.appendChild(body);
41366 createItemBody :function(bodyEl, id){
41367 var body = Roo.getDom(id);
41369 body = document.createElement("div");
41372 //Roo.fly(body).addClass("x-tabs-item-body");
41373 Roo.fly(body).addClass("tab-pane");
41374 bodyEl.insertBefore(body, bodyEl.firstChild);
41378 createStripElements : function(stripEl, text, closable, tpl)
41380 var td = document.createElement("li"); // was td..
41381 td.className = 'nav-item';
41383 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41386 stripEl.appendChild(td);
41388 td.className = "x-tabs-closable";
41389 if(!this.closeTpl){
41390 this.closeTpl = new Roo.Template(
41391 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41392 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41393 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41396 var el = this.closeTpl.overwrite(td, {"text": text});
41397 var close = el.getElementsByTagName("div")[0];
41398 var inner = el.getElementsByTagName("em")[0];
41399 return {"el": el, "close": close, "inner": inner};
41402 // not sure what this is..
41403 // if(!this.tabTpl){
41404 //this.tabTpl = new Roo.Template(
41405 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41406 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41408 // this.tabTpl = new Roo.Template(
41409 // '<a href="#">' +
41410 // '<span unselectable="on"' +
41411 // (this.disableTooltips ? '' : ' title="{text}"') +
41412 // ' >{text}</span></a>'
41418 var template = tpl || this.tabTpl || false;
41421 template = new Roo.Template(
41422 Roo.bootstrap.version == 4 ?
41424 '<a class="nav-link" href="#" unselectable="on"' +
41425 (this.disableTooltips ? '' : ' title="{text}"') +
41428 '<a class="nav-link" href="#">' +
41429 '<span unselectable="on"' +
41430 (this.disableTooltips ? '' : ' title="{text}"') +
41431 ' >{text}</span></a>'
41436 switch (typeof(template)) {
41440 template = new Roo.Template(template);
41446 var el = template.overwrite(td, {"text": text});
41448 var inner = el.getElementsByTagName("span")[0];
41450 return {"el": el, "inner": inner};
41458 * @class Roo.TabPanelItem
41459 * @extends Roo.util.Observable
41460 * Represents an individual item (tab plus body) in a TabPanel.
41461 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41462 * @param {String} id The id of this TabPanelItem
41463 * @param {String} text The text for the tab of this TabPanelItem
41464 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41466 Roo.bootstrap.panel.TabItem = function(config){
41468 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41469 * @type Roo.TabPanel
41471 this.tabPanel = config.panel;
41473 * The id for this TabPanelItem
41476 this.id = config.id;
41478 this.disabled = false;
41480 this.text = config.text;
41482 this.loaded = false;
41483 this.closable = config.closable;
41486 * The body element for this TabPanelItem.
41487 * @type Roo.Element
41489 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41490 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41491 this.bodyEl.setStyle("display", "block");
41492 this.bodyEl.setStyle("zoom", "1");
41493 //this.hideAction();
41495 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41497 this.el = Roo.get(els.el);
41498 this.inner = Roo.get(els.inner, true);
41499 this.textEl = Roo.bootstrap.version == 4 ?
41500 this.el : Roo.get(this.el.dom.firstChild, true);
41502 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41503 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41506 // this.el.on("mousedown", this.onTabMouseDown, this);
41507 this.el.on("click", this.onTabClick, this);
41509 if(config.closable){
41510 var c = Roo.get(els.close, true);
41511 c.dom.title = this.closeText;
41512 c.addClassOnOver("close-over");
41513 c.on("click", this.closeClick, this);
41519 * Fires when this tab becomes the active tab.
41520 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41521 * @param {Roo.TabPanelItem} this
41525 * @event beforeclose
41526 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41527 * @param {Roo.TabPanelItem} this
41528 * @param {Object} e Set cancel to true on this object to cancel the close.
41530 "beforeclose": true,
41533 * Fires when this tab is closed.
41534 * @param {Roo.TabPanelItem} this
41538 * @event deactivate
41539 * Fires when this tab is no longer the active tab.
41540 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41541 * @param {Roo.TabPanelItem} this
41543 "deactivate" : true
41545 this.hidden = false;
41547 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41550 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41552 purgeListeners : function(){
41553 Roo.util.Observable.prototype.purgeListeners.call(this);
41554 this.el.removeAllListeners();
41557 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41560 this.status_node.addClass("active");
41563 this.tabPanel.stripWrap.repaint();
41565 this.fireEvent("activate", this.tabPanel, this);
41569 * Returns true if this tab is the active tab.
41570 * @return {Boolean}
41572 isActive : function(){
41573 return this.tabPanel.getActiveTab() == this;
41577 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41580 this.status_node.removeClass("active");
41582 this.fireEvent("deactivate", this.tabPanel, this);
41585 hideAction : function(){
41586 this.bodyEl.hide();
41587 this.bodyEl.setStyle("position", "absolute");
41588 this.bodyEl.setLeft("-20000px");
41589 this.bodyEl.setTop("-20000px");
41592 showAction : function(){
41593 this.bodyEl.setStyle("position", "relative");
41594 this.bodyEl.setTop("");
41595 this.bodyEl.setLeft("");
41596 this.bodyEl.show();
41600 * Set the tooltip for the tab.
41601 * @param {String} tooltip The tab's tooltip
41603 setTooltip : function(text){
41604 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41605 this.textEl.dom.qtip = text;
41606 this.textEl.dom.removeAttribute('title');
41608 this.textEl.dom.title = text;
41612 onTabClick : function(e){
41613 e.preventDefault();
41614 this.tabPanel.activate(this.id);
41617 onTabMouseDown : function(e){
41618 e.preventDefault();
41619 this.tabPanel.activate(this.id);
41622 getWidth : function(){
41623 return this.inner.getWidth();
41626 setWidth : function(width){
41627 var iwidth = width - this.linode.getPadding("lr");
41628 this.inner.setWidth(iwidth);
41629 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41630 this.linode.setWidth(width);
41634 * Show or hide the tab
41635 * @param {Boolean} hidden True to hide or false to show.
41637 setHidden : function(hidden){
41638 this.hidden = hidden;
41639 this.linode.setStyle("display", hidden ? "none" : "");
41643 * Returns true if this tab is "hidden"
41644 * @return {Boolean}
41646 isHidden : function(){
41647 return this.hidden;
41651 * Returns the text for this tab
41654 getText : function(){
41658 autoSize : function(){
41659 //this.el.beginMeasure();
41660 this.textEl.setWidth(1);
41662 * #2804 [new] Tabs in Roojs
41663 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41665 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41666 //this.el.endMeasure();
41670 * Sets the text for the tab (Note: this also sets the tooltip text)
41671 * @param {String} text The tab's text and tooltip
41673 setText : function(text){
41675 this.textEl.update(text);
41676 this.setTooltip(text);
41677 //if(!this.tabPanel.resizeTabs){
41678 // this.autoSize();
41682 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41684 activate : function(){
41685 this.tabPanel.activate(this.id);
41689 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41691 disable : function(){
41692 if(this.tabPanel.active != this){
41693 this.disabled = true;
41694 this.status_node.addClass("disabled");
41699 * Enables this TabPanelItem if it was previously disabled.
41701 enable : function(){
41702 this.disabled = false;
41703 this.status_node.removeClass("disabled");
41707 * Sets the content for this TabPanelItem.
41708 * @param {String} content The content
41709 * @param {Boolean} loadScripts true to look for and load scripts
41711 setContent : function(content, loadScripts){
41712 this.bodyEl.update(content, loadScripts);
41716 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41717 * @return {Roo.UpdateManager} The UpdateManager
41719 getUpdateManager : function(){
41720 return this.bodyEl.getUpdateManager();
41724 * Set a URL to be used to load the content for this TabPanelItem.
41725 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41726 * @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)
41727 * @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)
41728 * @return {Roo.UpdateManager} The UpdateManager
41730 setUrl : function(url, params, loadOnce){
41731 if(this.refreshDelegate){
41732 this.un('activate', this.refreshDelegate);
41734 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41735 this.on("activate", this.refreshDelegate);
41736 return this.bodyEl.getUpdateManager();
41740 _handleRefresh : function(url, params, loadOnce){
41741 if(!loadOnce || !this.loaded){
41742 var updater = this.bodyEl.getUpdateManager();
41743 updater.update(url, params, this._setLoaded.createDelegate(this));
41748 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41749 * Will fail silently if the setUrl method has not been called.
41750 * This does not activate the panel, just updates its content.
41752 refresh : function(){
41753 if(this.refreshDelegate){
41754 this.loaded = false;
41755 this.refreshDelegate();
41760 _setLoaded : function(){
41761 this.loaded = true;
41765 closeClick : function(e){
41768 this.fireEvent("beforeclose", this, o);
41769 if(o.cancel !== true){
41770 this.tabPanel.removeTab(this.id);
41774 * The text displayed in the tooltip for the close icon.
41777 closeText : "Close this tab"
41780 * This script refer to:
41781 * Title: International Telephone Input
41782 * Author: Jack O'Connor
41783 * Code version: v12.1.12
41784 * Availability: https://github.com/jackocnr/intl-tel-input.git
41787 Roo.bootstrap.PhoneInputData = function() {
41790 "Afghanistan (افغانستان)",
41795 "Albania (Shqipëri)",
41800 "Algeria (الجزائر)",
41825 "Antigua and Barbuda",
41835 "Armenia (Հայաստան)",
41851 "Austria (Österreich)",
41856 "Azerbaijan (Azərbaycan)",
41866 "Bahrain (البحرين)",
41871 "Bangladesh (বাংলাদেশ)",
41881 "Belarus (Беларусь)",
41886 "Belgium (België)",
41916 "Bosnia and Herzegovina (Босна и Херцеговина)",
41931 "British Indian Ocean Territory",
41936 "British Virgin Islands",
41946 "Bulgaria (България)",
41956 "Burundi (Uburundi)",
41961 "Cambodia (កម្ពុជា)",
41966 "Cameroon (Cameroun)",
41975 ["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"]
41978 "Cape Verde (Kabu Verdi)",
41983 "Caribbean Netherlands",
41994 "Central African Republic (République centrafricaine)",
42014 "Christmas Island",
42020 "Cocos (Keeling) Islands",
42031 "Comoros (جزر القمر)",
42036 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42041 "Congo (Republic) (Congo-Brazzaville)",
42061 "Croatia (Hrvatska)",
42082 "Czech Republic (Česká republika)",
42087 "Denmark (Danmark)",
42102 "Dominican Republic (República Dominicana)",
42106 ["809", "829", "849"]
42124 "Equatorial Guinea (Guinea Ecuatorial)",
42144 "Falkland Islands (Islas Malvinas)",
42149 "Faroe Islands (Føroyar)",
42170 "French Guiana (Guyane française)",
42175 "French Polynesia (Polynésie française)",
42190 "Georgia (საქართველო)",
42195 "Germany (Deutschland)",
42215 "Greenland (Kalaallit Nunaat)",
42252 "Guinea-Bissau (Guiné Bissau)",
42277 "Hungary (Magyarország)",
42282 "Iceland (Ísland)",
42302 "Iraq (العراق)",
42318 "Israel (ישראל)",
42345 "Jordan (الأردن)",
42350 "Kazakhstan (Казахстан)",
42371 "Kuwait (الكويت)",
42376 "Kyrgyzstan (Кыргызстан)",
42386 "Latvia (Latvija)",
42391 "Lebanon (لبنان)",
42406 "Libya (ليبيا)",
42416 "Lithuania (Lietuva)",
42431 "Macedonia (FYROM) (Македонија)",
42436 "Madagascar (Madagasikara)",
42466 "Marshall Islands",
42476 "Mauritania (موريتانيا)",
42481 "Mauritius (Moris)",
42502 "Moldova (Republica Moldova)",
42512 "Mongolia (Монгол)",
42517 "Montenegro (Crna Gora)",
42527 "Morocco (المغرب)",
42533 "Mozambique (Moçambique)",
42538 "Myanmar (Burma) (မြန်မာ)",
42543 "Namibia (Namibië)",
42558 "Netherlands (Nederland)",
42563 "New Caledonia (Nouvelle-Calédonie)",
42598 "North Korea (조선 민주주의 인민 공화국)",
42603 "Northern Mariana Islands",
42619 "Pakistan (پاکستان)",
42629 "Palestine (فلسطين)",
42639 "Papua New Guinea",
42681 "Réunion (La Réunion)",
42687 "Romania (România)",
42703 "Saint Barthélemy",
42714 "Saint Kitts and Nevis",
42724 "Saint Martin (Saint-Martin (partie française))",
42730 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42735 "Saint Vincent and the Grenadines",
42750 "São Tomé and Príncipe (São Tomé e Príncipe)",
42755 "Saudi Arabia (المملكة العربية السعودية)",
42760 "Senegal (Sénégal)",
42790 "Slovakia (Slovensko)",
42795 "Slovenia (Slovenija)",
42805 "Somalia (Soomaaliya)",
42815 "South Korea (대한민국)",
42820 "South Sudan (جنوب السودان)",
42830 "Sri Lanka (ශ්රී ලංකාව)",
42835 "Sudan (السودان)",
42845 "Svalbard and Jan Mayen",
42856 "Sweden (Sverige)",
42861 "Switzerland (Schweiz)",
42866 "Syria (سوريا)",
42911 "Trinidad and Tobago",
42916 "Tunisia (تونس)",
42921 "Turkey (Türkiye)",
42931 "Turks and Caicos Islands",
42941 "U.S. Virgin Islands",
42951 "Ukraine (Україна)",
42956 "United Arab Emirates (الإمارات العربية المتحدة)",
42978 "Uzbekistan (Oʻzbekiston)",
42988 "Vatican City (Città del Vaticano)",
42999 "Vietnam (Việt Nam)",
43004 "Wallis and Futuna (Wallis-et-Futuna)",
43009 "Western Sahara (الصحراء الغربية)",
43015 "Yemen (اليمن)",
43039 * This script refer to:
43040 * Title: International Telephone Input
43041 * Author: Jack O'Connor
43042 * Code version: v12.1.12
43043 * Availability: https://github.com/jackocnr/intl-tel-input.git
43047 * @class Roo.bootstrap.PhoneInput
43048 * @extends Roo.bootstrap.TriggerField
43049 * An input with International dial-code selection
43051 * @cfg {String} defaultDialCode default '+852'
43052 * @cfg {Array} preferedCountries default []
43055 * Create a new PhoneInput.
43056 * @param {Object} config Configuration options
43059 Roo.bootstrap.PhoneInput = function(config) {
43060 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43063 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43065 listWidth: undefined,
43067 selectedClass: 'active',
43069 invalidClass : "has-warning",
43071 validClass: 'has-success',
43073 allowed: '0123456789',
43078 * @cfg {String} defaultDialCode The default dial code when initializing the input
43080 defaultDialCode: '+852',
43083 * @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
43085 preferedCountries: false,
43087 getAutoCreate : function()
43089 var data = Roo.bootstrap.PhoneInputData();
43090 var align = this.labelAlign || this.parentLabelAlign();
43093 this.allCountries = [];
43094 this.dialCodeMapping = [];
43096 for (var i = 0; i < data.length; i++) {
43098 this.allCountries[i] = {
43102 priority: c[3] || 0,
43103 areaCodes: c[4] || null
43105 this.dialCodeMapping[c[2]] = {
43108 priority: c[3] || 0,
43109 areaCodes: c[4] || null
43121 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43122 maxlength: this.max_length,
43123 cls : 'form-control tel-input',
43124 autocomplete: 'new-password'
43127 var hiddenInput = {
43130 cls: 'hidden-tel-input'
43134 hiddenInput.name = this.name;
43137 if (this.disabled) {
43138 input.disabled = true;
43141 var flag_container = {
43158 cls: this.hasFeedback ? 'has-feedback' : '',
43164 cls: 'dial-code-holder',
43171 cls: 'roo-select2-container input-group',
43178 if (this.fieldLabel.length) {
43181 tooltip: 'This field is required'
43187 cls: 'control-label',
43193 html: this.fieldLabel
43196 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43202 if(this.indicatorpos == 'right') {
43203 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43210 if(align == 'left') {
43218 if(this.labelWidth > 12){
43219 label.style = "width: " + this.labelWidth + 'px';
43221 if(this.labelWidth < 13 && this.labelmd == 0){
43222 this.labelmd = this.labelWidth;
43224 if(this.labellg > 0){
43225 label.cls += ' col-lg-' + this.labellg;
43226 input.cls += ' col-lg-' + (12 - this.labellg);
43228 if(this.labelmd > 0){
43229 label.cls += ' col-md-' + this.labelmd;
43230 container.cls += ' col-md-' + (12 - this.labelmd);
43232 if(this.labelsm > 0){
43233 label.cls += ' col-sm-' + this.labelsm;
43234 container.cls += ' col-sm-' + (12 - this.labelsm);
43236 if(this.labelxs > 0){
43237 label.cls += ' col-xs-' + this.labelxs;
43238 container.cls += ' col-xs-' + (12 - this.labelxs);
43248 var settings = this;
43250 ['xs','sm','md','lg'].map(function(size){
43251 if (settings[size]) {
43252 cfg.cls += ' col-' + size + '-' + settings[size];
43256 this.store = new Roo.data.Store({
43257 proxy : new Roo.data.MemoryProxy({}),
43258 reader : new Roo.data.JsonReader({
43269 'name' : 'dialCode',
43273 'name' : 'priority',
43277 'name' : 'areaCodes',
43284 if(!this.preferedCountries) {
43285 this.preferedCountries = [
43292 var p = this.preferedCountries.reverse();
43295 for (var i = 0; i < p.length; i++) {
43296 for (var j = 0; j < this.allCountries.length; j++) {
43297 if(this.allCountries[j].iso2 == p[i]) {
43298 var t = this.allCountries[j];
43299 this.allCountries.splice(j,1);
43300 this.allCountries.unshift(t);
43306 this.store.proxy.data = {
43308 data: this.allCountries
43314 initEvents : function()
43317 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43319 this.indicator = this.indicatorEl();
43320 this.flag = this.flagEl();
43321 this.dialCodeHolder = this.dialCodeHolderEl();
43323 this.trigger = this.el.select('div.flag-box',true).first();
43324 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43329 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43330 _this.list.setWidth(lw);
43333 this.list.on('mouseover', this.onViewOver, this);
43334 this.list.on('mousemove', this.onViewMove, this);
43335 this.inputEl().on("keyup", this.onKeyUp, this);
43336 this.inputEl().on("keypress", this.onKeyPress, this);
43338 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43340 this.view = new Roo.View(this.list, this.tpl, {
43341 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43344 this.view.on('click', this.onViewClick, this);
43345 this.setValue(this.defaultDialCode);
43348 onTriggerClick : function(e)
43350 Roo.log('trigger click');
43355 if(this.isExpanded()){
43357 this.hasFocus = false;
43359 this.store.load({});
43360 this.hasFocus = true;
43365 isExpanded : function()
43367 return this.list.isVisible();
43370 collapse : function()
43372 if(!this.isExpanded()){
43376 Roo.get(document).un('mousedown', this.collapseIf, this);
43377 Roo.get(document).un('mousewheel', this.collapseIf, this);
43378 this.fireEvent('collapse', this);
43382 expand : function()
43386 if(this.isExpanded() || !this.hasFocus){
43390 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43391 this.list.setWidth(lw);
43394 this.restrictHeight();
43396 Roo.get(document).on('mousedown', this.collapseIf, this);
43397 Roo.get(document).on('mousewheel', this.collapseIf, this);
43399 this.fireEvent('expand', this);
43402 restrictHeight : function()
43404 this.list.alignTo(this.inputEl(), this.listAlign);
43405 this.list.alignTo(this.inputEl(), this.listAlign);
43408 onViewOver : function(e, t)
43410 if(this.inKeyMode){
43413 var item = this.view.findItemFromChild(t);
43416 var index = this.view.indexOf(item);
43417 this.select(index, false);
43422 onViewClick : function(view, doFocus, el, e)
43424 var index = this.view.getSelectedIndexes()[0];
43426 var r = this.store.getAt(index);
43429 this.onSelect(r, index);
43431 if(doFocus !== false && !this.blockFocus){
43432 this.inputEl().focus();
43436 onViewMove : function(e, t)
43438 this.inKeyMode = false;
43441 select : function(index, scrollIntoView)
43443 this.selectedIndex = index;
43444 this.view.select(index);
43445 if(scrollIntoView !== false){
43446 var el = this.view.getNode(index);
43448 this.list.scrollChildIntoView(el, false);
43453 createList : function()
43455 this.list = Roo.get(document.body).createChild({
43457 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43458 style: 'display:none'
43461 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43464 collapseIf : function(e)
43466 var in_combo = e.within(this.el);
43467 var in_list = e.within(this.list);
43468 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43470 if (in_combo || in_list || is_list) {
43476 onSelect : function(record, index)
43478 if(this.fireEvent('beforeselect', this, record, index) !== false){
43480 this.setFlagClass(record.data.iso2);
43481 this.setDialCode(record.data.dialCode);
43482 this.hasFocus = false;
43484 this.fireEvent('select', this, record, index);
43488 flagEl : function()
43490 var flag = this.el.select('div.flag',true).first();
43497 dialCodeHolderEl : function()
43499 var d = this.el.select('input.dial-code-holder',true).first();
43506 setDialCode : function(v)
43508 this.dialCodeHolder.dom.value = '+'+v;
43511 setFlagClass : function(n)
43513 this.flag.dom.className = 'flag '+n;
43516 getValue : function()
43518 var v = this.inputEl().getValue();
43519 if(this.dialCodeHolder) {
43520 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43525 setValue : function(v)
43527 var d = this.getDialCode(v);
43529 //invalid dial code
43530 if(v.length == 0 || !d || d.length == 0) {
43532 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43533 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43539 this.setFlagClass(this.dialCodeMapping[d].iso2);
43540 this.setDialCode(d);
43541 this.inputEl().dom.value = v.replace('+'+d,'');
43542 this.hiddenEl().dom.value = this.getValue();
43547 getDialCode : function(v)
43551 if (v.length == 0) {
43552 return this.dialCodeHolder.dom.value;
43556 if (v.charAt(0) != "+") {
43559 var numericChars = "";
43560 for (var i = 1; i < v.length; i++) {
43561 var c = v.charAt(i);
43564 if (this.dialCodeMapping[numericChars]) {
43565 dialCode = v.substr(1, i);
43567 if (numericChars.length == 4) {
43577 this.setValue(this.defaultDialCode);
43581 hiddenEl : function()
43583 return this.el.select('input.hidden-tel-input',true).first();
43586 // after setting val
43587 onKeyUp : function(e){
43588 this.setValue(this.getValue());
43591 onKeyPress : function(e){
43592 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43599 * @class Roo.bootstrap.MoneyField
43600 * @extends Roo.bootstrap.ComboBox
43601 * Bootstrap MoneyField class
43604 * Create a new MoneyField.
43605 * @param {Object} config Configuration options
43608 Roo.bootstrap.MoneyField = function(config) {
43610 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43614 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43617 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43619 allowDecimals : true,
43621 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43623 decimalSeparator : ".",
43625 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43627 decimalPrecision : 0,
43629 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43631 allowNegative : true,
43633 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43637 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43639 minValue : Number.NEGATIVE_INFINITY,
43641 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43643 maxValue : Number.MAX_VALUE,
43645 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43647 minText : "The minimum value for this field is {0}",
43649 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43651 maxText : "The maximum value for this field is {0}",
43653 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43654 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43656 nanText : "{0} is not a valid number",
43658 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43662 * @cfg {String} defaults currency of the MoneyField
43663 * value should be in lkey
43665 defaultCurrency : false,
43667 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43669 thousandsDelimiter : false,
43671 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43682 getAutoCreate : function()
43684 var align = this.labelAlign || this.parentLabelAlign();
43696 cls : 'form-control roo-money-amount-input',
43697 autocomplete: 'new-password'
43700 var hiddenInput = {
43704 cls: 'hidden-number-input'
43707 if(this.max_length) {
43708 input.maxlength = this.max_length;
43712 hiddenInput.name = this.name;
43715 if (this.disabled) {
43716 input.disabled = true;
43719 var clg = 12 - this.inputlg;
43720 var cmd = 12 - this.inputmd;
43721 var csm = 12 - this.inputsm;
43722 var cxs = 12 - this.inputxs;
43726 cls : 'row roo-money-field',
43730 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43734 cls: 'roo-select2-container input-group',
43738 cls : 'form-control roo-money-currency-input',
43739 autocomplete: 'new-password',
43741 name : this.currencyName
43745 cls : 'input-group-addon',
43759 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43763 cls: this.hasFeedback ? 'has-feedback' : '',
43774 if (this.fieldLabel.length) {
43777 tooltip: 'This field is required'
43783 cls: 'control-label',
43789 html: this.fieldLabel
43792 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43798 if(this.indicatorpos == 'right') {
43799 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43806 if(align == 'left') {
43814 if(this.labelWidth > 12){
43815 label.style = "width: " + this.labelWidth + 'px';
43817 if(this.labelWidth < 13 && this.labelmd == 0){
43818 this.labelmd = this.labelWidth;
43820 if(this.labellg > 0){
43821 label.cls += ' col-lg-' + this.labellg;
43822 input.cls += ' col-lg-' + (12 - this.labellg);
43824 if(this.labelmd > 0){
43825 label.cls += ' col-md-' + this.labelmd;
43826 container.cls += ' col-md-' + (12 - this.labelmd);
43828 if(this.labelsm > 0){
43829 label.cls += ' col-sm-' + this.labelsm;
43830 container.cls += ' col-sm-' + (12 - this.labelsm);
43832 if(this.labelxs > 0){
43833 label.cls += ' col-xs-' + this.labelxs;
43834 container.cls += ' col-xs-' + (12 - this.labelxs);
43845 var settings = this;
43847 ['xs','sm','md','lg'].map(function(size){
43848 if (settings[size]) {
43849 cfg.cls += ' col-' + size + '-' + settings[size];
43856 initEvents : function()
43858 this.indicator = this.indicatorEl();
43860 this.initCurrencyEvent();
43862 this.initNumberEvent();
43865 initCurrencyEvent : function()
43868 throw "can not find store for combo";
43871 this.store = Roo.factory(this.store, Roo.data);
43872 this.store.parent = this;
43876 this.triggerEl = this.el.select('.input-group-addon', true).first();
43878 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43883 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43884 _this.list.setWidth(lw);
43887 this.list.on('mouseover', this.onViewOver, this);
43888 this.list.on('mousemove', this.onViewMove, this);
43889 this.list.on('scroll', this.onViewScroll, this);
43892 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43895 this.view = new Roo.View(this.list, this.tpl, {
43896 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43899 this.view.on('click', this.onViewClick, this);
43901 this.store.on('beforeload', this.onBeforeLoad, this);
43902 this.store.on('load', this.onLoad, this);
43903 this.store.on('loadexception', this.onLoadException, this);
43905 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43906 "up" : function(e){
43907 this.inKeyMode = true;
43911 "down" : function(e){
43912 if(!this.isExpanded()){
43913 this.onTriggerClick();
43915 this.inKeyMode = true;
43920 "enter" : function(e){
43923 if(this.fireEvent("specialkey", this, e)){
43924 this.onViewClick(false);
43930 "esc" : function(e){
43934 "tab" : function(e){
43937 if(this.fireEvent("specialkey", this, e)){
43938 this.onViewClick(false);
43946 doRelay : function(foo, bar, hname){
43947 if(hname == 'down' || this.scope.isExpanded()){
43948 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43956 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43960 initNumberEvent : function(e)
43962 this.inputEl().on("keydown" , this.fireKey, this);
43963 this.inputEl().on("focus", this.onFocus, this);
43964 this.inputEl().on("blur", this.onBlur, this);
43966 this.inputEl().relayEvent('keyup', this);
43968 if(this.indicator){
43969 this.indicator.addClass('invisible');
43972 this.originalValue = this.getValue();
43974 if(this.validationEvent == 'keyup'){
43975 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43976 this.inputEl().on('keyup', this.filterValidation, this);
43978 else if(this.validationEvent !== false){
43979 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43982 if(this.selectOnFocus){
43983 this.on("focus", this.preFocus, this);
43986 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43987 this.inputEl().on("keypress", this.filterKeys, this);
43989 this.inputEl().relayEvent('keypress', this);
43992 var allowed = "0123456789";
43994 if(this.allowDecimals){
43995 allowed += this.decimalSeparator;
43998 if(this.allowNegative){
44002 if(this.thousandsDelimiter) {
44006 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44008 var keyPress = function(e){
44010 var k = e.getKey();
44012 var c = e.getCharCode();
44015 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44016 allowed.indexOf(String.fromCharCode(c)) === -1
44022 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44026 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44031 this.inputEl().on("keypress", keyPress, this);
44035 onTriggerClick : function(e)
44042 this.loadNext = false;
44044 if(this.isExpanded()){
44049 this.hasFocus = true;
44051 if(this.triggerAction == 'all') {
44052 this.doQuery(this.allQuery, true);
44056 this.doQuery(this.getRawValue());
44059 getCurrency : function()
44061 var v = this.currencyEl().getValue();
44066 restrictHeight : function()
44068 this.list.alignTo(this.currencyEl(), this.listAlign);
44069 this.list.alignTo(this.currencyEl(), this.listAlign);
44072 onViewClick : function(view, doFocus, el, e)
44074 var index = this.view.getSelectedIndexes()[0];
44076 var r = this.store.getAt(index);
44079 this.onSelect(r, index);
44083 onSelect : function(record, index){
44085 if(this.fireEvent('beforeselect', this, record, index) !== false){
44087 this.setFromCurrencyData(index > -1 ? record.data : false);
44091 this.fireEvent('select', this, record, index);
44095 setFromCurrencyData : function(o)
44099 this.lastCurrency = o;
44101 if (this.currencyField) {
44102 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44104 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44107 this.lastSelectionText = currency;
44109 //setting default currency
44110 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44111 this.setCurrency(this.defaultCurrency);
44115 this.setCurrency(currency);
44118 setFromData : function(o)
44122 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44124 this.setFromCurrencyData(c);
44129 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44131 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44134 this.setValue(value);
44138 setCurrency : function(v)
44140 this.currencyValue = v;
44143 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44148 setValue : function(v)
44150 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44156 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44158 this.inputEl().dom.value = (v == '') ? '' :
44159 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44161 if(!this.allowZero && v === '0') {
44162 this.hiddenEl().dom.value = '';
44163 this.inputEl().dom.value = '';
44170 getRawValue : function()
44172 var v = this.inputEl().getValue();
44177 getValue : function()
44179 return this.fixPrecision(this.parseValue(this.getRawValue()));
44182 parseValue : function(value)
44184 if(this.thousandsDelimiter) {
44186 r = new RegExp(",", "g");
44187 value = value.replace(r, "");
44190 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44191 return isNaN(value) ? '' : value;
44195 fixPrecision : function(value)
44197 if(this.thousandsDelimiter) {
44199 r = new RegExp(",", "g");
44200 value = value.replace(r, "");
44203 var nan = isNaN(value);
44205 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44206 return nan ? '' : value;
44208 return parseFloat(value).toFixed(this.decimalPrecision);
44211 decimalPrecisionFcn : function(v)
44213 return Math.floor(v);
44216 validateValue : function(value)
44218 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44222 var num = this.parseValue(value);
44225 this.markInvalid(String.format(this.nanText, value));
44229 if(num < this.minValue){
44230 this.markInvalid(String.format(this.minText, this.minValue));
44234 if(num > this.maxValue){
44235 this.markInvalid(String.format(this.maxText, this.maxValue));
44242 validate : function()
44244 if(this.disabled || this.allowBlank){
44249 var currency = this.getCurrency();
44251 if(this.validateValue(this.getRawValue()) && currency.length){
44256 this.markInvalid();
44260 getName: function()
44265 beforeBlur : function()
44271 var v = this.parseValue(this.getRawValue());
44278 onBlur : function()
44282 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44283 //this.el.removeClass(this.focusClass);
44286 this.hasFocus = false;
44288 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44292 var v = this.getValue();
44294 if(String(v) !== String(this.startValue)){
44295 this.fireEvent('change', this, v, this.startValue);
44298 this.fireEvent("blur", this);
44301 inputEl : function()
44303 return this.el.select('.roo-money-amount-input', true).first();
44306 currencyEl : function()
44308 return this.el.select('.roo-money-currency-input', true).first();
44311 hiddenEl : function()
44313 return this.el.select('input.hidden-number-input',true).first();
44317 * @class Roo.bootstrap.BezierSignature
44318 * @extends Roo.bootstrap.Component
44319 * Bootstrap BezierSignature class
44320 * This script refer to:
44321 * Title: Signature Pad
44323 * Availability: https://github.com/szimek/signature_pad
44326 * Create a new BezierSignature
44327 * @param {Object} config The config object
44330 Roo.bootstrap.BezierSignature = function(config){
44331 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44337 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44344 mouse_btn_down: true,
44347 * @cfg {int} canvas height
44349 canvas_height: '200px',
44352 * @cfg {float|function} Radius of a single dot.
44357 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44362 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44367 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44372 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44377 * @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.
44379 bg_color: 'rgba(0, 0, 0, 0)',
44382 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44384 dot_color: 'black',
44387 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44389 velocity_filter_weight: 0.7,
44392 * @cfg {function} Callback when stroke begin.
44397 * @cfg {function} Callback when stroke end.
44401 getAutoCreate : function()
44403 var cls = 'roo-signature column';
44406 cls += ' ' + this.cls;
44416 for(var i = 0; i < col_sizes.length; i++) {
44417 if(this[col_sizes[i]]) {
44418 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44428 cls: 'roo-signature-body',
44432 cls: 'roo-signature-body-canvas',
44433 height: this.canvas_height,
44434 width: this.canvas_width
44441 style: 'display: none'
44449 initEvents: function()
44451 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44453 var canvas = this.canvasEl();
44455 // mouse && touch event swapping...
44456 canvas.dom.style.touchAction = 'none';
44457 canvas.dom.style.msTouchAction = 'none';
44459 this.mouse_btn_down = false;
44460 canvas.on('mousedown', this._handleMouseDown, this);
44461 canvas.on('mousemove', this._handleMouseMove, this);
44462 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44464 if (window.PointerEvent) {
44465 canvas.on('pointerdown', this._handleMouseDown, this);
44466 canvas.on('pointermove', this._handleMouseMove, this);
44467 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44470 if ('ontouchstart' in window) {
44471 canvas.on('touchstart', this._handleTouchStart, this);
44472 canvas.on('touchmove', this._handleTouchMove, this);
44473 canvas.on('touchend', this._handleTouchEnd, this);
44476 Roo.EventManager.onWindowResize(this.resize, this, true);
44478 // file input event
44479 this.fileEl().on('change', this.uploadImage, this);
44486 resize: function(){
44488 var canvas = this.canvasEl().dom;
44489 var ctx = this.canvasElCtx();
44490 var img_data = false;
44492 if(canvas.width > 0) {
44493 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44495 // setting canvas width will clean img data
44498 var style = window.getComputedStyle ?
44499 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44501 var padding_left = parseInt(style.paddingLeft) || 0;
44502 var padding_right = parseInt(style.paddingRight) || 0;
44504 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44507 ctx.putImageData(img_data, 0, 0);
44511 _handleMouseDown: function(e)
44513 if (e.browserEvent.which === 1) {
44514 this.mouse_btn_down = true;
44515 this.strokeBegin(e);
44519 _handleMouseMove: function (e)
44521 if (this.mouse_btn_down) {
44522 this.strokeMoveUpdate(e);
44526 _handleMouseUp: function (e)
44528 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44529 this.mouse_btn_down = false;
44534 _handleTouchStart: function (e) {
44536 e.preventDefault();
44537 if (e.browserEvent.targetTouches.length === 1) {
44538 // var touch = e.browserEvent.changedTouches[0];
44539 // this.strokeBegin(touch);
44541 this.strokeBegin(e); // assume e catching the correct xy...
44545 _handleTouchMove: function (e) {
44546 e.preventDefault();
44547 // var touch = event.targetTouches[0];
44548 // _this._strokeMoveUpdate(touch);
44549 this.strokeMoveUpdate(e);
44552 _handleTouchEnd: function (e) {
44553 var wasCanvasTouched = e.target === this.canvasEl().dom;
44554 if (wasCanvasTouched) {
44555 e.preventDefault();
44556 // var touch = event.changedTouches[0];
44557 // _this._strokeEnd(touch);
44562 reset: function () {
44563 this._lastPoints = [];
44564 this._lastVelocity = 0;
44565 this._lastWidth = (this.min_width + this.max_width) / 2;
44566 this.canvasElCtx().fillStyle = this.dot_color;
44569 strokeMoveUpdate: function(e)
44571 this.strokeUpdate(e);
44573 if (this.throttle) {
44574 this.throttleStroke(this.strokeUpdate, this.throttle);
44577 this.strokeUpdate(e);
44581 strokeBegin: function(e)
44583 var newPointGroup = {
44584 color: this.dot_color,
44588 if (typeof this.onBegin === 'function') {
44592 this.curve_data.push(newPointGroup);
44594 this.strokeUpdate(e);
44597 strokeUpdate: function(e)
44599 var rect = this.canvasEl().dom.getBoundingClientRect();
44600 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44601 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44602 var lastPoints = lastPointGroup.points;
44603 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44604 var isLastPointTooClose = lastPoint
44605 ? point.distanceTo(lastPoint) <= this.min_distance
44607 var color = lastPointGroup.color;
44608 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44609 var curve = this.addPoint(point);
44611 this.drawDot({color: color, point: point});
44614 this.drawCurve({color: color, curve: curve});
44624 strokeEnd: function(e)
44626 this.strokeUpdate(e);
44627 if (typeof this.onEnd === 'function') {
44632 addPoint: function (point) {
44633 var _lastPoints = this._lastPoints;
44634 _lastPoints.push(point);
44635 if (_lastPoints.length > 2) {
44636 if (_lastPoints.length === 3) {
44637 _lastPoints.unshift(_lastPoints[0]);
44639 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44640 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44641 _lastPoints.shift();
44647 calculateCurveWidths: function (startPoint, endPoint) {
44648 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44649 (1 - this.velocity_filter_weight) * this._lastVelocity;
44651 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44654 start: this._lastWidth
44657 this._lastVelocity = velocity;
44658 this._lastWidth = newWidth;
44662 drawDot: function (_a) {
44663 var color = _a.color, point = _a.point;
44664 var ctx = this.canvasElCtx();
44665 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44667 this.drawCurveSegment(point.x, point.y, width);
44669 ctx.fillStyle = color;
44673 drawCurve: function (_a) {
44674 var color = _a.color, curve = _a.curve;
44675 var ctx = this.canvasElCtx();
44676 var widthDelta = curve.endWidth - curve.startWidth;
44677 var drawSteps = Math.floor(curve.length()) * 2;
44679 ctx.fillStyle = color;
44680 for (var i = 0; i < drawSteps; i += 1) {
44681 var t = i / drawSteps;
44687 var x = uuu * curve.startPoint.x;
44688 x += 3 * uu * t * curve.control1.x;
44689 x += 3 * u * tt * curve.control2.x;
44690 x += ttt * curve.endPoint.x;
44691 var y = uuu * curve.startPoint.y;
44692 y += 3 * uu * t * curve.control1.y;
44693 y += 3 * u * tt * curve.control2.y;
44694 y += ttt * curve.endPoint.y;
44695 var width = curve.startWidth + ttt * widthDelta;
44696 this.drawCurveSegment(x, y, width);
44702 drawCurveSegment: function (x, y, width) {
44703 var ctx = this.canvasElCtx();
44705 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44706 this.is_empty = false;
44711 var ctx = this.canvasElCtx();
44712 var canvas = this.canvasEl().dom;
44713 ctx.fillStyle = this.bg_color;
44714 ctx.clearRect(0, 0, canvas.width, canvas.height);
44715 ctx.fillRect(0, 0, canvas.width, canvas.height);
44716 this.curve_data = [];
44718 this.is_empty = true;
44723 return this.el.select('input',true).first();
44726 canvasEl: function()
44728 return this.el.select('canvas',true).first();
44731 canvasElCtx: function()
44733 return this.el.select('canvas',true).first().dom.getContext('2d');
44736 getImage: function(type)
44738 if(this.is_empty) {
44743 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44746 drawFromImage: function(img_src)
44748 var img = new Image();
44750 img.onload = function(){
44751 this.canvasElCtx().drawImage(img, 0, 0);
44756 this.is_empty = false;
44759 selectImage: function()
44761 this.fileEl().dom.click();
44764 uploadImage: function(e)
44766 var reader = new FileReader();
44768 reader.onload = function(e){
44769 var img = new Image();
44770 img.onload = function(){
44772 this.canvasElCtx().drawImage(img, 0, 0);
44774 img.src = e.target.result;
44777 reader.readAsDataURL(e.target.files[0]);
44780 // Bezier Point Constructor
44781 Point: (function () {
44782 function Point(x, y, time) {
44785 this.time = time || Date.now();
44787 Point.prototype.distanceTo = function (start) {
44788 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44790 Point.prototype.equals = function (other) {
44791 return this.x === other.x && this.y === other.y && this.time === other.time;
44793 Point.prototype.velocityFrom = function (start) {
44794 return this.time !== start.time
44795 ? this.distanceTo(start) / (this.time - start.time)
44802 // Bezier Constructor
44803 Bezier: (function () {
44804 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44805 this.startPoint = startPoint;
44806 this.control2 = control2;
44807 this.control1 = control1;
44808 this.endPoint = endPoint;
44809 this.startWidth = startWidth;
44810 this.endWidth = endWidth;
44812 Bezier.fromPoints = function (points, widths, scope) {
44813 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44814 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44815 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44817 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44818 var dx1 = s1.x - s2.x;
44819 var dy1 = s1.y - s2.y;
44820 var dx2 = s2.x - s3.x;
44821 var dy2 = s2.y - s3.y;
44822 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44823 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44824 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44825 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44826 var dxm = m1.x - m2.x;
44827 var dym = m1.y - m2.y;
44828 var k = l2 / (l1 + l2);
44829 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44830 var tx = s2.x - cm.x;
44831 var ty = s2.y - cm.y;
44833 c1: new scope.Point(m1.x + tx, m1.y + ty),
44834 c2: new scope.Point(m2.x + tx, m2.y + ty)
44837 Bezier.prototype.length = function () {
44842 for (var i = 0; i <= steps; i += 1) {
44844 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44845 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44847 var xdiff = cx - px;
44848 var ydiff = cy - py;
44849 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44856 Bezier.prototype.point = function (t, start, c1, c2, end) {
44857 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44858 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44859 + (3.0 * c2 * (1.0 - t) * t * t)
44860 + (end * t * t * t);
44865 throttleStroke: function(fn, wait) {
44866 if (wait === void 0) { wait = 250; }
44868 var timeout = null;
44872 var later = function () {
44873 previous = Date.now();
44875 result = fn.apply(storedContext, storedArgs);
44877 storedContext = null;
44881 return function wrapper() {
44883 for (var _i = 0; _i < arguments.length; _i++) {
44884 args[_i] = arguments[_i];
44886 var now = Date.now();
44887 var remaining = wait - (now - previous);
44888 storedContext = this;
44890 if (remaining <= 0 || remaining > wait) {
44892 clearTimeout(timeout);
44896 result = fn.apply(storedContext, storedArgs);
44898 storedContext = null;
44902 else if (!timeout) {
44903 timeout = window.setTimeout(later, remaining);