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.
3586 * @param {Object} config The config object
3590 Roo.bootstrap.Menu = function(config){
3591 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3592 if (this.registerMenu && this.type != 'treeview') {
3593 Roo.bootstrap.MenuMgr.register(this);
3600 * Fires before this menu is displayed (return false to block)
3601 * @param {Roo.menu.Menu} this
3606 * Fires before this menu is hidden (return false to block)
3607 * @param {Roo.menu.Menu} this
3612 * Fires after this menu is displayed
3613 * @param {Roo.menu.Menu} this
3618 * Fires after this menu is hidden
3619 * @param {Roo.menu.Menu} this
3624 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3625 * @param {Roo.menu.Menu} this
3626 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3627 * @param {Roo.EventObject} e
3632 * Fires when the mouse is hovering over this menu
3633 * @param {Roo.menu.Menu} this
3634 * @param {Roo.EventObject} e
3635 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3640 * Fires when the mouse exits 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 a menu item contained in this menu is clicked
3649 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3650 * @param {Roo.EventObject} e
3654 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3657 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3661 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3664 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3666 registerMenu : true,
3668 menuItems :false, // stores the menu items..
3678 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3680 hideTrigger : false,
3683 getChildContainer : function() {
3687 getAutoCreate : function(){
3689 //if (['right'].indexOf(this.align)!==-1) {
3690 // cfg.cn[1].cls += ' pull-right'
3696 cls : 'dropdown-menu shadow' ,
3697 style : 'z-index:1000'
3701 if (this.type === 'submenu') {
3702 cfg.cls = 'submenu active';
3704 if (this.type === 'treeview') {
3705 cfg.cls = 'treeview-menu';
3710 initEvents : function() {
3712 // Roo.log("ADD event");
3713 // Roo.log(this.triggerEl.dom);
3715 this.triggerEl.on('click', this.onTriggerClick, this);
3717 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3719 if (!this.hideTrigger) {
3720 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3721 // dropdown toggle on the 'a' in BS4?
3722 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3724 this.triggerEl.addClass('dropdown-toggle');
3728 this.el.on('touchstart' , this.onTouch, this);
3730 this.el.on('click' , this.onClick, this);
3732 this.el.on("mouseover", this.onMouseOver, this);
3733 this.el.on("mouseout", this.onMouseOut, this);
3737 findTargetItem : function(e)
3739 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3743 //Roo.log(t); Roo.log(t.id);
3745 //Roo.log(this.menuitems);
3746 return this.menuitems.get(t.id);
3748 //return this.items.get(t.menuItemId);
3754 onTouch : function(e)
3756 Roo.log("menu.onTouch");
3757 //e.stopEvent(); this make the user popdown broken
3761 onClick : function(e)
3763 Roo.log("menu.onClick");
3765 var t = this.findTargetItem(e);
3766 if(!t || t.isContainer){
3771 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3772 if(t == this.activeItem && t.shouldDeactivate(e)){
3773 this.activeItem.deactivate();
3774 delete this.activeItem;
3778 this.setActiveItem(t, true);
3786 Roo.log('pass click event');
3790 this.fireEvent("click", this, t, e);
3794 if(!t.href.length || t.href == '#'){
3795 (function() { _this.hide(); }).defer(100);
3800 onMouseOver : function(e){
3801 var t = this.findTargetItem(e);
3804 // if(t.canActivate && !t.disabled){
3805 // this.setActiveItem(t, true);
3809 this.fireEvent("mouseover", this, e, t);
3811 isVisible : function(){
3812 return !this.hidden;
3814 onMouseOut : function(e){
3815 var t = this.findTargetItem(e);
3818 // if(t == this.activeItem && t.shouldDeactivate(e)){
3819 // this.activeItem.deactivate();
3820 // delete this.activeItem;
3823 this.fireEvent("mouseout", this, e, t);
3828 * Displays this menu relative to another element
3829 * @param {String/HTMLElement/Roo.Element} element The element to align to
3830 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3831 * the element (defaults to this.defaultAlign)
3832 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3834 show : function(el, pos, parentMenu)
3836 if (false === this.fireEvent("beforeshow", this)) {
3837 Roo.log("show canceled");
3840 this.parentMenu = parentMenu;
3845 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3848 * Displays this menu at a specific xy position
3849 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3850 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3852 showAt : function(xy, parentMenu, /* private: */_e){
3853 this.parentMenu = parentMenu;
3858 this.fireEvent("beforeshow", this);
3859 //xy = this.el.adjustForConstraints(xy);
3863 this.hideMenuItems();
3864 this.hidden = false;
3865 this.triggerEl.addClass('open');
3866 this.el.addClass('show');
3868 // reassign x when hitting right
3869 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3870 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3873 // reassign y when hitting bottom
3874 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3875 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3878 // but the list may align on trigger left or trigger top... should it be a properity?
3880 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3885 this.fireEvent("show", this);
3891 this.doFocus.defer(50, this);
3895 doFocus : function(){
3897 this.focusEl.focus();
3902 * Hides this menu and optionally all parent menus
3903 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3905 hide : function(deep)
3907 if (false === this.fireEvent("beforehide", this)) {
3908 Roo.log("hide canceled");
3911 this.hideMenuItems();
3912 if(this.el && this.isVisible()){
3914 if(this.activeItem){
3915 this.activeItem.deactivate();
3916 this.activeItem = null;
3918 this.triggerEl.removeClass('open');;
3919 this.el.removeClass('show');
3921 this.fireEvent("hide", this);
3923 if(deep === true && this.parentMenu){
3924 this.parentMenu.hide(true);
3928 onTriggerClick : function(e)
3930 Roo.log('trigger click');
3932 var target = e.getTarget();
3934 Roo.log(target.nodeName.toLowerCase());
3936 if(target.nodeName.toLowerCase() === 'i'){
3942 onTriggerPress : function(e)
3944 Roo.log('trigger press');
3945 //Roo.log(e.getTarget());
3946 // Roo.log(this.triggerEl.dom);
3948 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3949 var pel = Roo.get(e.getTarget());
3950 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3951 Roo.log('is treeview or dropdown?');
3955 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3959 if (this.isVisible()) {
3964 this.show(this.triggerEl, '?', false);
3967 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3974 hideMenuItems : function()
3976 Roo.log("hide Menu Items");
3981 this.el.select('.open',true).each(function(aa) {
3983 aa.removeClass('open');
3987 addxtypeChild : function (tree, cntr) {
3988 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3990 this.menuitems.add(comp);
4002 this.getEl().dom.innerHTML = '';
4003 this.menuitems.clear();
4017 * @class Roo.bootstrap.MenuItem
4018 * @extends Roo.bootstrap.Component
4019 * Bootstrap MenuItem class
4020 * @cfg {String} html the menu label
4021 * @cfg {String} href the link
4022 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4023 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4024 * @cfg {Boolean} active used on sidebars to highlight active itesm
4025 * @cfg {String} fa favicon to show on left of menu item.
4026 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4030 * Create a new MenuItem
4031 * @param {Object} config The config object
4035 Roo.bootstrap.MenuItem = function(config){
4036 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4041 * The raw click event for the entire grid.
4042 * @param {Roo.bootstrap.MenuItem} this
4043 * @param {Roo.EventObject} e
4049 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4053 preventDefault: false,
4054 isContainer : false,
4058 getAutoCreate : function(){
4060 if(this.isContainer){
4063 cls: 'dropdown-menu-item '
4073 cls : 'dropdown-item',
4078 if (this.fa !== false) {
4081 cls : 'fa fa-' + this.fa
4090 cls: 'dropdown-menu-item',
4093 if (this.parent().type == 'treeview') {
4094 cfg.cls = 'treeview-menu';
4097 cfg.cls += ' active';
4102 anc.href = this.href || cfg.cn[0].href ;
4103 ctag.html = this.html || cfg.cn[0].html ;
4107 initEvents: function()
4109 if (this.parent().type == 'treeview') {
4110 this.el.select('a').on('click', this.onClick, this);
4114 this.menu.parentType = this.xtype;
4115 this.menu.triggerEl = this.el;
4116 this.menu = this.addxtype(Roo.apply({}, this.menu));
4120 onClick : function(e)
4122 Roo.log('item on click ');
4124 if(this.preventDefault){
4127 //this.parent().hideMenuItems();
4129 this.fireEvent('click', this, e);
4148 * @class Roo.bootstrap.MenuSeparator
4149 * @extends Roo.bootstrap.Component
4150 * Bootstrap MenuSeparator class
4153 * Create a new MenuItem
4154 * @param {Object} config The config object
4158 Roo.bootstrap.MenuSeparator = function(config){
4159 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4162 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4164 getAutoCreate : function(){
4183 * @class Roo.bootstrap.Modal
4184 * @extends Roo.bootstrap.Component
4185 * Bootstrap Modal class
4186 * @cfg {String} title Title of dialog
4187 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4188 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4189 * @cfg {Boolean} specificTitle default false
4190 * @cfg {Array} buttons Array of buttons or standard button set..
4191 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4192 * @cfg {Boolean} animate default true
4193 * @cfg {Boolean} allow_close default true
4194 * @cfg {Boolean} fitwindow default false
4195 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4196 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4197 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4198 * @cfg {String} size (sm|lg|xl) default empty
4199 * @cfg {Number} max_width set the max width of modal
4200 * @cfg {Boolean} editableTitle can the title be edited
4205 * Create a new Modal Dialog
4206 * @param {Object} config The config object
4209 Roo.bootstrap.Modal = function(config){
4210 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4215 * The raw btnclick event for the button
4216 * @param {Roo.EventObject} e
4221 * Fire when dialog resize
4222 * @param {Roo.bootstrap.Modal} this
4223 * @param {Roo.EventObject} e
4227 * @event titlechanged
4228 * Fire when the editable title has been changed
4229 * @param {Roo.bootstrap.Modal} this
4230 * @param {Roo.EventObject} value
4232 "titlechanged" : true
4235 this.buttons = this.buttons || [];
4238 this.tmpl = Roo.factory(this.tmpl);
4243 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4245 title : 'test dialog',
4255 specificTitle: false,
4257 buttonPosition: 'right',
4279 editableTitle : false,
4281 onRender : function(ct, position)
4283 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4286 var cfg = Roo.apply({}, this.getAutoCreate());
4289 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4291 //if (!cfg.name.length) {
4295 cfg.cls += ' ' + this.cls;
4298 cfg.style = this.style;
4300 this.el = Roo.get(document.body).createChild(cfg, position);
4302 //var type = this.el.dom.type;
4305 if(this.tabIndex !== undefined){
4306 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4309 this.dialogEl = this.el.select('.modal-dialog',true).first();
4310 this.bodyEl = this.el.select('.modal-body',true).first();
4311 this.closeEl = this.el.select('.modal-header .close', true).first();
4312 this.headerEl = this.el.select('.modal-header',true).first();
4313 this.titleEl = this.el.select('.modal-title',true).first();
4314 this.footerEl = this.el.select('.modal-footer',true).first();
4316 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4318 //this.el.addClass("x-dlg-modal");
4320 if (this.buttons.length) {
4321 Roo.each(this.buttons, function(bb) {
4322 var b = Roo.apply({}, bb);
4323 b.xns = b.xns || Roo.bootstrap;
4324 b.xtype = b.xtype || 'Button';
4325 if (typeof(b.listeners) == 'undefined') {
4326 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4329 var btn = Roo.factory(b);
4331 btn.render(this.getButtonContainer());
4335 // render the children.
4338 if(typeof(this.items) != 'undefined'){
4339 var items = this.items;
4342 for(var i =0;i < items.length;i++) {
4343 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4347 this.items = nitems;
4349 // where are these used - they used to be body/close/footer
4353 //this.el.addClass([this.fieldClass, this.cls]);
4357 getAutoCreate : function()
4359 // we will default to modal-body-overflow - might need to remove or make optional later.
4361 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4362 html : this.html || ''
4367 cls : 'modal-title',
4371 if(this.specificTitle){ // WTF is this?
4376 if (this.allow_close && Roo.bootstrap.version == 3) {
4386 if (this.editableTitle) {
4388 cls: 'form-control roo-editable-title d-none',
4394 if (this.allow_close && Roo.bootstrap.version == 4) {
4404 if(this.size.length){
4405 size = 'modal-' + this.size;
4408 var footer = Roo.bootstrap.version == 3 ?
4410 cls : 'modal-footer',
4414 cls: 'btn-' + this.buttonPosition
4419 { // BS4 uses mr-auto on left buttons....
4420 cls : 'modal-footer'
4431 cls: "modal-dialog " + size,
4434 cls : "modal-content",
4437 cls : 'modal-header',
4452 modal.cls += ' fade';
4458 getChildContainer : function() {
4463 getButtonContainer : function() {
4465 return Roo.bootstrap.version == 4 ?
4466 this.el.select('.modal-footer',true).first()
4467 : this.el.select('.modal-footer div',true).first();
4470 initEvents : function()
4472 if (this.allow_close) {
4473 this.closeEl.on('click', this.hide, this);
4475 Roo.EventManager.onWindowResize(this.resize, this, true);
4476 if (this.editableTitle) {
4477 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4478 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4479 this.headerEditEl.on('keyup', function(e) {
4480 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4481 this.toggleHeaderInput(false)
4484 this.headerEditEl.on('blur', function(e) {
4485 this.toggleHeaderInput(false)
4494 this.maskEl.setSize(
4495 Roo.lib.Dom.getViewWidth(true),
4496 Roo.lib.Dom.getViewHeight(true)
4499 if (this.fitwindow) {
4501 this.dialogEl.setStyle( { 'max-width' : '100%' });
4503 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4504 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4509 if(this.max_width !== 0) {
4511 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4514 this.setSize(w, this.height);
4518 if(this.max_height) {
4519 this.setSize(w,Math.min(
4521 Roo.lib.Dom.getViewportHeight(true) - 60
4527 if(!this.fit_content) {
4528 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4532 this.setSize(w, Math.min(
4534 this.headerEl.getHeight() +
4535 this.footerEl.getHeight() +
4536 this.getChildHeight(this.bodyEl.dom.childNodes),
4537 Roo.lib.Dom.getViewportHeight(true) - 60)
4543 setSize : function(w,h)
4554 if (!this.rendered) {
4557 this.toggleHeaderInput(false);
4558 //this.el.setStyle('display', 'block');
4559 this.el.removeClass('hideing');
4560 this.el.dom.style.display='block';
4562 Roo.get(document.body).addClass('modal-open');
4564 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4567 this.el.addClass('show');
4568 this.el.addClass('in');
4571 this.el.addClass('show');
4572 this.el.addClass('in');
4575 // not sure how we can show data in here..
4577 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4580 Roo.get(document.body).addClass("x-body-masked");
4582 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4583 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4584 this.maskEl.dom.style.display = 'block';
4585 this.maskEl.addClass('show');
4590 this.fireEvent('show', this);
4592 // set zindex here - otherwise it appears to be ignored...
4593 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4596 this.items.forEach( function(e) {
4597 e.layout ? e.layout() : false;
4605 if(this.fireEvent("beforehide", this) !== false){
4607 this.maskEl.removeClass('show');
4609 this.maskEl.dom.style.display = '';
4610 Roo.get(document.body).removeClass("x-body-masked");
4611 this.el.removeClass('in');
4612 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4614 if(this.animate){ // why
4615 this.el.addClass('hideing');
4616 this.el.removeClass('show');
4618 if (!this.el.hasClass('hideing')) {
4619 return; // it's been shown again...
4622 this.el.dom.style.display='';
4624 Roo.get(document.body).removeClass('modal-open');
4625 this.el.removeClass('hideing');
4629 this.el.removeClass('show');
4630 this.el.dom.style.display='';
4631 Roo.get(document.body).removeClass('modal-open');
4634 this.fireEvent('hide', this);
4637 isVisible : function()
4640 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4644 addButton : function(str, cb)
4648 var b = Roo.apply({}, { html : str } );
4649 b.xns = b.xns || Roo.bootstrap;
4650 b.xtype = b.xtype || 'Button';
4651 if (typeof(b.listeners) == 'undefined') {
4652 b.listeners = { click : cb.createDelegate(this) };
4655 var btn = Roo.factory(b);
4657 btn.render(this.getButtonContainer());
4663 setDefaultButton : function(btn)
4665 //this.el.select('.modal-footer').()
4668 resizeTo: function(w,h)
4670 this.dialogEl.setWidth(w);
4672 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4674 this.bodyEl.setHeight(h - diff);
4676 this.fireEvent('resize', this);
4679 setContentSize : function(w, h)
4683 onButtonClick: function(btn,e)
4686 this.fireEvent('btnclick', btn.name, e);
4689 * Set the title of the Dialog
4690 * @param {String} str new Title
4692 setTitle: function(str) {
4693 this.titleEl.dom.innerHTML = str;
4697 * Set the body of the Dialog
4698 * @param {String} str new Title
4700 setBody: function(str) {
4701 this.bodyEl.dom.innerHTML = str;
4704 * Set the body of the Dialog using the template
4705 * @param {Obj} data - apply this data to the template and replace the body contents.
4707 applyBody: function(obj)
4710 Roo.log("Error - using apply Body without a template");
4713 this.tmpl.overwrite(this.bodyEl, obj);
4716 getChildHeight : function(child_nodes)
4720 child_nodes.length == 0
4725 var child_height = 0;
4727 for(var i = 0; i < child_nodes.length; i++) {
4730 * for modal with tabs...
4731 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4733 var layout_childs = child_nodes[i].childNodes;
4735 for(var j = 0; j < layout_childs.length; j++) {
4737 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4739 var layout_body_childs = layout_childs[j].childNodes;
4741 for(var k = 0; k < layout_body_childs.length; k++) {
4743 if(layout_body_childs[k].classList.contains('navbar')) {
4744 child_height += layout_body_childs[k].offsetHeight;
4748 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4750 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4752 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4754 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4755 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4770 child_height += child_nodes[i].offsetHeight;
4771 // Roo.log(child_nodes[i].offsetHeight);
4774 return child_height;
4776 toggleHeaderInput : function(is_edit)
4778 if (!this.editableTitle) {
4779 return; // not editable.
4781 if (is_edit && this.is_header_editing) {
4782 return; // already editing..
4786 this.headerEditEl.dom.value = this.title;
4787 this.headerEditEl.removeClass('d-none');
4788 this.headerEditEl.dom.focus();
4789 this.titleEl.addClass('d-none');
4791 this.is_header_editing = true;
4794 // flip back to not editing.
4795 this.title = this.headerEditEl.dom.value;
4796 this.headerEditEl.addClass('d-none');
4797 this.titleEl.removeClass('d-none');
4798 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4799 this.is_header_editing = false;
4800 this.fireEvent('titlechanged', this, this.title);
4809 Roo.apply(Roo.bootstrap.Modal, {
4811 * Button config that displays a single OK button
4820 * Button config that displays Yes and No buttons
4836 * Button config that displays OK and Cancel buttons
4851 * Button config that displays Yes, No and Cancel buttons
4876 * messagebox - can be used as a replace
4880 * @class Roo.MessageBox
4881 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4885 Roo.Msg.alert('Status', 'Changes saved successfully.');
4887 // Prompt for user data:
4888 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4890 // process text value...
4894 // Show a dialog using config options:
4896 title:'Save Changes?',
4897 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4898 buttons: Roo.Msg.YESNOCANCEL,
4905 Roo.bootstrap.MessageBox = function(){
4906 var dlg, opt, mask, waitTimer;
4907 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4908 var buttons, activeTextEl, bwidth;
4912 var handleButton = function(button){
4914 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4918 var handleHide = function(){
4920 dlg.el.removeClass(opt.cls);
4923 // Roo.TaskMgr.stop(waitTimer);
4924 // waitTimer = null;
4929 var updateButtons = function(b){
4932 buttons["ok"].hide();
4933 buttons["cancel"].hide();
4934 buttons["yes"].hide();
4935 buttons["no"].hide();
4936 dlg.footerEl.hide();
4940 dlg.footerEl.show();
4941 for(var k in buttons){
4942 if(typeof buttons[k] != "function"){
4945 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4946 width += buttons[k].el.getWidth()+15;
4956 var handleEsc = function(d, k, e){
4957 if(opt && opt.closable !== false){
4967 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4968 * @return {Roo.BasicDialog} The BasicDialog element
4970 getDialog : function(){
4972 dlg = new Roo.bootstrap.Modal( {
4975 //constraintoviewport:false,
4977 //collapsible : false,
4982 //buttonAlign:"center",
4983 closeClick : function(){
4984 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4987 handleButton("cancel");
4992 dlg.on("hide", handleHide);
4994 //dlg.addKeyListener(27, handleEsc);
4996 this.buttons = buttons;
4997 var bt = this.buttonText;
4998 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4999 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5000 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5001 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5003 bodyEl = dlg.bodyEl.createChild({
5005 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5006 '<textarea class="roo-mb-textarea"></textarea>' +
5007 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5009 msgEl = bodyEl.dom.firstChild;
5010 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5011 textboxEl.enableDisplayMode();
5012 textboxEl.addKeyListener([10,13], function(){
5013 if(dlg.isVisible() && opt && opt.buttons){
5016 }else if(opt.buttons.yes){
5017 handleButton("yes");
5021 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5022 textareaEl.enableDisplayMode();
5023 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5024 progressEl.enableDisplayMode();
5026 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5027 var pf = progressEl.dom.firstChild;
5029 pp = Roo.get(pf.firstChild);
5030 pp.setHeight(pf.offsetHeight);
5038 * Updates the message box body text
5039 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5040 * the XHTML-compliant non-breaking space character '&#160;')
5041 * @return {Roo.MessageBox} This message box
5043 updateText : function(text)
5045 if(!dlg.isVisible() && !opt.width){
5046 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5047 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5049 msgEl.innerHTML = text || ' ';
5051 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5052 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5054 Math.min(opt.width || cw , this.maxWidth),
5055 Math.max(opt.minWidth || this.minWidth, bwidth)
5058 activeTextEl.setWidth(w);
5060 if(dlg.isVisible()){
5061 dlg.fixedcenter = false;
5063 // to big, make it scroll. = But as usual stupid IE does not support
5066 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5067 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5068 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5070 bodyEl.dom.style.height = '';
5071 bodyEl.dom.style.overflowY = '';
5074 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5076 bodyEl.dom.style.overflowX = '';
5079 dlg.setContentSize(w, bodyEl.getHeight());
5080 if(dlg.isVisible()){
5081 dlg.fixedcenter = true;
5087 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5088 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5089 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5090 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5091 * @return {Roo.MessageBox} This message box
5093 updateProgress : function(value, text){
5095 this.updateText(text);
5098 if (pp) { // weird bug on my firefox - for some reason this is not defined
5099 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5100 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5106 * Returns true if the message box is currently displayed
5107 * @return {Boolean} True if the message box is visible, else false
5109 isVisible : function(){
5110 return dlg && dlg.isVisible();
5114 * Hides the message box if it is displayed
5117 if(this.isVisible()){
5123 * Displays a new message box, or reinitializes an existing message box, based on the config options
5124 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5125 * The following config object properties are supported:
5127 Property Type Description
5128 ---------- --------------- ------------------------------------------------------------------------------------
5129 animEl String/Element An id or Element from which the message box should animate as it opens and
5130 closes (defaults to undefined)
5131 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5132 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5133 closable Boolean False to hide the top-right close button (defaults to true). Note that
5134 progress and wait dialogs will ignore this property and always hide the
5135 close button as they can only be closed programmatically.
5136 cls String A custom CSS class to apply to the message box element
5137 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5138 displayed (defaults to 75)
5139 fn Function A callback function to execute after closing the dialog. The arguments to the
5140 function will be btn (the name of the button that was clicked, if applicable,
5141 e.g. "ok"), and text (the value of the active text field, if applicable).
5142 Progress and wait dialogs will ignore this option since they do not respond to
5143 user actions and can only be closed programmatically, so any required function
5144 should be called by the same code after it closes the dialog.
5145 icon String A CSS class that provides a background image to be used as an icon for
5146 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5147 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5148 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5149 modal Boolean False to allow user interaction with the page while the message box is
5150 displayed (defaults to true)
5151 msg String A string that will replace the existing message box body text (defaults
5152 to the XHTML-compliant non-breaking space character ' ')
5153 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5154 progress Boolean True to display a progress bar (defaults to false)
5155 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5156 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5157 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5158 title String The title text
5159 value String The string value to set into the active textbox element if displayed
5160 wait Boolean True to display a progress bar (defaults to false)
5161 width Number The width of the dialog in pixels
5168 msg: 'Please enter your address:',
5170 buttons: Roo.MessageBox.OKCANCEL,
5173 animEl: 'addAddressBtn'
5176 * @param {Object} config Configuration options
5177 * @return {Roo.MessageBox} This message box
5179 show : function(options)
5182 // this causes nightmares if you show one dialog after another
5183 // especially on callbacks..
5185 if(this.isVisible()){
5188 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5189 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5190 Roo.log("New Dialog Message:" + options.msg )
5191 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5192 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5195 var d = this.getDialog();
5197 d.setTitle(opt.title || " ");
5198 d.closeEl.setDisplayed(opt.closable !== false);
5199 activeTextEl = textboxEl;
5200 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5205 textareaEl.setHeight(typeof opt.multiline == "number" ?
5206 opt.multiline : this.defaultTextHeight);
5207 activeTextEl = textareaEl;
5216 progressEl.setDisplayed(opt.progress === true);
5218 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5220 this.updateProgress(0);
5221 activeTextEl.dom.value = opt.value || "";
5223 dlg.setDefaultButton(activeTextEl);
5225 var bs = opt.buttons;
5229 }else if(bs && bs.yes){
5230 db = buttons["yes"];
5232 dlg.setDefaultButton(db);
5234 bwidth = updateButtons(opt.buttons);
5235 this.updateText(opt.msg);
5237 d.el.addClass(opt.cls);
5239 d.proxyDrag = opt.proxyDrag === true;
5240 d.modal = opt.modal !== false;
5241 d.mask = opt.modal !== false ? mask : false;
5243 // force it to the end of the z-index stack so it gets a cursor in FF
5244 document.body.appendChild(dlg.el.dom);
5245 d.animateTarget = null;
5246 d.show(options.animEl);
5252 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5253 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5254 * and closing the message box when the process is complete.
5255 * @param {String} title The title bar text
5256 * @param {String} msg The message box body text
5257 * @return {Roo.MessageBox} This message box
5259 progress : function(title, msg){
5266 minWidth: this.minProgressWidth,
5273 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5274 * If a callback function is passed it will be called after the user clicks the button, and the
5275 * id of the button that was clicked will be passed as the only parameter to the callback
5276 * (could also be the top-right close button).
5277 * @param {String} title The title bar text
5278 * @param {String} msg The message box body text
5279 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5280 * @param {Object} scope (optional) The scope of the callback function
5281 * @return {Roo.MessageBox} This message box
5283 alert : function(title, msg, fn, scope)
5298 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5299 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5300 * You are responsible for closing the message box when the process is complete.
5301 * @param {String} msg The message box body text
5302 * @param {String} title (optional) The title bar text
5303 * @return {Roo.MessageBox} This message box
5305 wait : function(msg, title){
5316 waitTimer = Roo.TaskMgr.start({
5318 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5326 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5327 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5328 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5329 * @param {String} title The title bar text
5330 * @param {String} msg The message box body text
5331 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5332 * @param {Object} scope (optional) The scope of the callback function
5333 * @return {Roo.MessageBox} This message box
5335 confirm : function(title, msg, fn, scope){
5339 buttons: this.YESNO,
5348 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5349 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5350 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5351 * (could also be the top-right close button) and the text that was entered will be passed as the two
5352 * parameters to the callback.
5353 * @param {String} title The title bar text
5354 * @param {String} msg The message box body text
5355 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5356 * @param {Object} scope (optional) The scope of the callback function
5357 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5358 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5359 * @return {Roo.MessageBox} This message box
5361 prompt : function(title, msg, fn, scope, multiline){
5365 buttons: this.OKCANCEL,
5370 multiline: multiline,
5377 * Button config that displays a single OK button
5382 * Button config that displays Yes and No buttons
5385 YESNO : {yes:true, no:true},
5387 * Button config that displays OK and Cancel buttons
5390 OKCANCEL : {ok:true, cancel:true},
5392 * Button config that displays Yes, No and Cancel buttons
5395 YESNOCANCEL : {yes:true, no:true, cancel:true},
5398 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5401 defaultTextHeight : 75,
5403 * The maximum width in pixels of the message box (defaults to 600)
5408 * The minimum width in pixels of the message box (defaults to 100)
5413 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5414 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5417 minProgressWidth : 250,
5419 * An object containing the default button text strings that can be overriden for localized language support.
5420 * Supported properties are: ok, cancel, yes and no.
5421 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5434 * Shorthand for {@link Roo.MessageBox}
5436 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5437 Roo.Msg = Roo.Msg || Roo.MessageBox;
5446 * @class Roo.bootstrap.Navbar
5447 * @extends Roo.bootstrap.Component
5448 * Bootstrap Navbar class
5451 * Create a new Navbar
5452 * @param {Object} config The config object
5456 Roo.bootstrap.Navbar = function(config){
5457 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5461 * @event beforetoggle
5462 * Fire before toggle the menu
5463 * @param {Roo.EventObject} e
5465 "beforetoggle" : true
5469 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5478 getAutoCreate : function(){
5481 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5485 initEvents :function ()
5487 //Roo.log(this.el.select('.navbar-toggle',true));
5488 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5495 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5497 var size = this.el.getSize();
5498 this.maskEl.setSize(size.width, size.height);
5499 this.maskEl.enableDisplayMode("block");
5508 getChildContainer : function()
5510 if (this.el && this.el.select('.collapse').getCount()) {
5511 return this.el.select('.collapse',true).first();
5526 onToggle : function()
5529 if(this.fireEvent('beforetoggle', this) === false){
5532 var ce = this.el.select('.navbar-collapse',true).first();
5534 if (!ce.hasClass('show')) {
5544 * Expand the navbar pulldown
5546 expand : function ()
5549 var ce = this.el.select('.navbar-collapse',true).first();
5550 if (ce.hasClass('collapsing')) {
5553 ce.dom.style.height = '';
5555 ce.addClass('in'); // old...
5556 ce.removeClass('collapse');
5557 ce.addClass('show');
5558 var h = ce.getHeight();
5560 ce.removeClass('show');
5561 // at this point we should be able to see it..
5562 ce.addClass('collapsing');
5564 ce.setHeight(0); // resize it ...
5565 ce.on('transitionend', function() {
5566 //Roo.log('done transition');
5567 ce.removeClass('collapsing');
5568 ce.addClass('show');
5569 ce.removeClass('collapse');
5571 ce.dom.style.height = '';
5572 }, this, { single: true} );
5574 ce.dom.scrollTop = 0;
5577 * Collapse the navbar pulldown
5579 collapse : function()
5581 var ce = this.el.select('.navbar-collapse',true).first();
5583 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5584 // it's collapsed or collapsing..
5587 ce.removeClass('in'); // old...
5588 ce.setHeight(ce.getHeight());
5589 ce.removeClass('show');
5590 ce.addClass('collapsing');
5592 ce.on('transitionend', function() {
5593 ce.dom.style.height = '';
5594 ce.removeClass('collapsing');
5595 ce.addClass('collapse');
5596 }, this, { single: true} );
5616 * @class Roo.bootstrap.NavSimplebar
5617 * @extends Roo.bootstrap.Navbar
5618 * Bootstrap Sidebar class
5620 * @cfg {Boolean} inverse is inverted color
5622 * @cfg {String} type (nav | pills | tabs)
5623 * @cfg {Boolean} arrangement stacked | justified
5624 * @cfg {String} align (left | right) alignment
5626 * @cfg {Boolean} main (true|false) main nav bar? default false
5627 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5629 * @cfg {String} tag (header|footer|nav|div) default is nav
5631 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5635 * Create a new Sidebar
5636 * @param {Object} config The config object
5640 Roo.bootstrap.NavSimplebar = function(config){
5641 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5644 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5660 getAutoCreate : function(){
5664 tag : this.tag || 'div',
5665 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5667 if (['light','white'].indexOf(this.weight) > -1) {
5668 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5670 cfg.cls += ' bg-' + this.weight;
5673 cfg.cls += ' navbar-inverse';
5677 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5679 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5688 cls: 'nav nav-' + this.xtype,
5694 this.type = this.type || 'nav';
5695 if (['tabs','pills'].indexOf(this.type) != -1) {
5696 cfg.cn[0].cls += ' nav-' + this.type
5700 if (this.type!=='nav') {
5701 Roo.log('nav type must be nav/tabs/pills')
5703 cfg.cn[0].cls += ' navbar-nav'
5709 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5710 cfg.cn[0].cls += ' nav-' + this.arrangement;
5714 if (this.align === 'right') {
5715 cfg.cn[0].cls += ' navbar-right';
5740 * navbar-expand-md fixed-top
5744 * @class Roo.bootstrap.NavHeaderbar
5745 * @extends Roo.bootstrap.NavSimplebar
5746 * Bootstrap Sidebar class
5748 * @cfg {String} brand what is brand
5749 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5750 * @cfg {String} brand_href href of the brand
5751 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5752 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5753 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5754 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5757 * Create a new Sidebar
5758 * @param {Object} config The config object
5762 Roo.bootstrap.NavHeaderbar = function(config){
5763 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5767 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5774 desktopCenter : false,
5777 getAutoCreate : function(){
5780 tag: this.nav || 'nav',
5781 cls: 'navbar navbar-expand-md',
5787 if (this.desktopCenter) {
5788 cn.push({cls : 'container', cn : []});
5796 cls: 'navbar-toggle navbar-toggler',
5797 'data-toggle': 'collapse',
5802 html: 'Toggle navigation'
5806 cls: 'icon-bar navbar-toggler-icon'
5819 cn.push( Roo.bootstrap.version == 4 ? btn : {
5821 cls: 'navbar-header',
5830 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5834 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5836 if (['light','white'].indexOf(this.weight) > -1) {
5837 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5839 cfg.cls += ' bg-' + this.weight;
5842 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5843 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5845 // tag can override this..
5847 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5850 if (this.brand !== '') {
5851 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5852 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5854 href: this.brand_href ? this.brand_href : '#',
5855 cls: 'navbar-brand',
5863 cfg.cls += ' main-nav';
5871 getHeaderChildContainer : function()
5873 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5874 return this.el.select('.navbar-header',true).first();
5877 return this.getChildContainer();
5880 getChildContainer : function()
5883 return this.el.select('.roo-navbar-collapse',true).first();
5888 initEvents : function()
5890 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5892 if (this.autohide) {
5897 Roo.get(document).on('scroll',function(e) {
5898 var ns = Roo.get(document).getScroll().top;
5899 var os = prevScroll;
5903 ft.removeClass('slideDown');
5904 ft.addClass('slideUp');
5907 ft.removeClass('slideUp');
5908 ft.addClass('slideDown');
5929 * @class Roo.bootstrap.NavSidebar
5930 * @extends Roo.bootstrap.Navbar
5931 * Bootstrap Sidebar class
5934 * Create a new Sidebar
5935 * @param {Object} config The config object
5939 Roo.bootstrap.NavSidebar = function(config){
5940 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5943 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5945 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5947 getAutoCreate : function(){
5952 cls: 'sidebar sidebar-nav'
5974 * @class Roo.bootstrap.NavGroup
5975 * @extends Roo.bootstrap.Component
5976 * Bootstrap NavGroup class
5977 * @cfg {String} align (left|right)
5978 * @cfg {Boolean} inverse
5979 * @cfg {String} type (nav|pills|tab) default nav
5980 * @cfg {String} navId - reference Id for navbar.
5981 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5984 * Create a new nav group
5985 * @param {Object} config The config object
5988 Roo.bootstrap.NavGroup = function(config){
5989 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5992 Roo.bootstrap.NavGroup.register(this);
5996 * Fires when the active item changes
5997 * @param {Roo.bootstrap.NavGroup} this
5998 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5999 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6006 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6018 getAutoCreate : function()
6020 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6026 if (Roo.bootstrap.version == 4) {
6027 if (['tabs','pills'].indexOf(this.type) != -1) {
6028 cfg.cls += ' nav-' + this.type;
6030 // trying to remove so header bar can right align top?
6031 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6032 // do not use on header bar...
6033 cfg.cls += ' navbar-nav';
6038 if (['tabs','pills'].indexOf(this.type) != -1) {
6039 cfg.cls += ' nav-' + this.type
6041 if (this.type !== 'nav') {
6042 Roo.log('nav type must be nav/tabs/pills')
6044 cfg.cls += ' navbar-nav'
6048 if (this.parent() && this.parent().sidebar) {
6051 cls: 'dashboard-menu sidebar-menu'
6057 if (this.form === true) {
6060 cls: 'navbar-form form-inline'
6062 //nav navbar-right ml-md-auto
6063 if (this.align === 'right') {
6064 cfg.cls += ' navbar-right ml-md-auto';
6066 cfg.cls += ' navbar-left';
6070 if (this.align === 'right') {
6071 cfg.cls += ' navbar-right ml-md-auto';
6073 cfg.cls += ' mr-auto';
6077 cfg.cls += ' navbar-inverse';
6085 * sets the active Navigation item
6086 * @param {Roo.bootstrap.NavItem} the new current navitem
6088 setActiveItem : function(item)
6091 Roo.each(this.navItems, function(v){
6096 v.setActive(false, true);
6103 item.setActive(true, true);
6104 this.fireEvent('changed', this, item, prev);
6109 * gets the active Navigation item
6110 * @return {Roo.bootstrap.NavItem} the current navitem
6112 getActive : function()
6116 Roo.each(this.navItems, function(v){
6127 indexOfNav : function()
6131 Roo.each(this.navItems, function(v,i){
6142 * adds a Navigation item
6143 * @param {Roo.bootstrap.NavItem} the navitem to add
6145 addItem : function(cfg)
6147 if (this.form && Roo.bootstrap.version == 4) {
6150 var cn = new Roo.bootstrap.NavItem(cfg);
6152 cn.parentId = this.id;
6153 cn.onRender(this.el, null);
6157 * register a Navigation item
6158 * @param {Roo.bootstrap.NavItem} the navitem to add
6160 register : function(item)
6162 this.navItems.push( item);
6163 item.navId = this.navId;
6168 * clear all the Navigation item
6171 clearAll : function()
6174 this.el.dom.innerHTML = '';
6177 getNavItem: function(tabId)
6180 Roo.each(this.navItems, function(e) {
6181 if (e.tabId == tabId) {
6191 setActiveNext : function()
6193 var i = this.indexOfNav(this.getActive());
6194 if (i > this.navItems.length) {
6197 this.setActiveItem(this.navItems[i+1]);
6199 setActivePrev : function()
6201 var i = this.indexOfNav(this.getActive());
6205 this.setActiveItem(this.navItems[i-1]);
6207 clearWasActive : function(except) {
6208 Roo.each(this.navItems, function(e) {
6209 if (e.tabId != except.tabId && e.was_active) {
6210 e.was_active = false;
6217 getWasActive : function ()
6220 Roo.each(this.navItems, function(e) {
6235 Roo.apply(Roo.bootstrap.NavGroup, {
6239 * register a Navigation Group
6240 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6242 register : function(navgrp)
6244 this.groups[navgrp.navId] = navgrp;
6248 * fetch a Navigation Group based on the navigation ID
6249 * @param {string} the navgroup to add
6250 * @returns {Roo.bootstrap.NavGroup} the navgroup
6252 get: function(navId) {
6253 if (typeof(this.groups[navId]) == 'undefined') {
6255 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6257 return this.groups[navId] ;
6272 * @class Roo.bootstrap.NavItem
6273 * @extends Roo.bootstrap.Component
6274 * Bootstrap Navbar.NavItem class
6275 * @cfg {String} href link to
6276 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6277 * @cfg {Boolean} button_outline show and outlined button
6278 * @cfg {String} html content of button
6279 * @cfg {String} badge text inside badge
6280 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6281 * @cfg {String} glyphicon DEPRICATED - use fa
6282 * @cfg {String} icon DEPRICATED - use fa
6283 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6284 * @cfg {Boolean} active Is item active
6285 * @cfg {Boolean} disabled Is item disabled
6286 * @cfg {String} linkcls Link Class
6287 * @cfg {Boolean} preventDefault (true | false) default false
6288 * @cfg {String} tabId the tab that this item activates.
6289 * @cfg {String} tagtype (a|span) render as a href or span?
6290 * @cfg {Boolean} animateRef (true|false) link to element default false
6293 * Create a new Navbar Item
6294 * @param {Object} config The config object
6296 Roo.bootstrap.NavItem = function(config){
6297 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6302 * The raw click event for the entire grid.
6303 * @param {Roo.EventObject} e
6308 * Fires when the active item active state changes
6309 * @param {Roo.bootstrap.NavItem} this
6310 * @param {boolean} state the new state
6316 * Fires when scroll to element
6317 * @param {Roo.bootstrap.NavItem} this
6318 * @param {Object} options
6319 * @param {Roo.EventObject} e
6327 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6336 preventDefault : false,
6344 button_outline : false,
6348 getAutoCreate : function(){
6355 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6358 cfg.cls += ' active' ;
6360 if (this.disabled) {
6361 cfg.cls += ' disabled';
6365 if (this.button_weight.length) {
6366 cfg.tag = this.href ? 'a' : 'button';
6367 cfg.html = this.html || '';
6368 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6370 cfg.href = this.href;
6373 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6375 cfg.cls += " nav-html";
6378 // menu .. should add dropdown-menu class - so no need for carat..
6380 if (this.badge !== '') {
6382 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6387 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6391 href : this.href || "#",
6392 html: this.html || '',
6396 if (this.tagtype == 'a') {
6397 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6401 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6402 } else if (this.fa) {
6403 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6404 } else if(this.glyphicon) {
6405 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6407 cfg.cn[0].cls += " nav-html";
6411 cfg.cn[0].html += " <span class='caret'></span>";
6415 if (this.badge !== '') {
6416 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6424 onRender : function(ct, position)
6426 // Roo.log("Call onRender: " + this.xtype);
6427 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6431 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6432 this.navLink = this.el.select('.nav-link',true).first();
6433 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6438 initEvents: function()
6440 if (typeof (this.menu) != 'undefined') {
6441 this.menu.parentType = this.xtype;
6442 this.menu.triggerEl = this.el;
6443 this.menu = this.addxtype(Roo.apply({}, this.menu));
6446 this.el.on('click', this.onClick, this);
6448 //if(this.tagtype == 'span'){
6449 // this.el.select('span',true).on('click', this.onClick, this);
6452 // at this point parent should be available..
6453 this.parent().register(this);
6456 onClick : function(e)
6458 if (e.getTarget('.dropdown-menu-item')) {
6459 // did you click on a menu itemm.... - then don't trigger onclick..
6464 this.preventDefault ||
6467 Roo.log("NavItem - prevent Default?");
6471 if (this.disabled) {
6475 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6476 if (tg && tg.transition) {
6477 Roo.log("waiting for the transitionend");
6483 //Roo.log("fire event clicked");
6484 if(this.fireEvent('click', this, e) === false){
6488 if(this.tagtype == 'span'){
6492 //Roo.log(this.href);
6493 var ael = this.el.select('a',true).first();
6496 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6497 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6498 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6499 return; // ignore... - it's a 'hash' to another page.
6501 Roo.log("NavItem - prevent Default?");
6503 this.scrollToElement(e);
6507 var p = this.parent();
6509 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6510 if (typeof(p.setActiveItem) !== 'undefined') {
6511 p.setActiveItem(this);
6515 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6516 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6517 // remove the collapsed menu expand...
6518 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6522 isActive: function () {
6525 setActive : function(state, fire, is_was_active)
6527 if (this.active && !state && this.navId) {
6528 this.was_active = true;
6529 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6531 nv.clearWasActive(this);
6535 this.active = state;
6538 this.el.removeClass('active');
6539 this.navLink ? this.navLink.removeClass('active') : false;
6540 } else if (!this.el.hasClass('active')) {
6542 this.el.addClass('active');
6543 if (Roo.bootstrap.version == 4 && this.navLink ) {
6544 this.navLink.addClass('active');
6549 this.fireEvent('changed', this, state);
6552 // show a panel if it's registered and related..
6554 if (!this.navId || !this.tabId || !state || is_was_active) {
6558 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6562 var pan = tg.getPanelByName(this.tabId);
6566 // if we can not flip to new panel - go back to old nav highlight..
6567 if (false == tg.showPanel(pan)) {
6568 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6570 var onav = nv.getWasActive();
6572 onav.setActive(true, false, true);
6581 // this should not be here...
6582 setDisabled : function(state)
6584 this.disabled = state;
6586 this.el.removeClass('disabled');
6587 } else if (!this.el.hasClass('disabled')) {
6588 this.el.addClass('disabled');
6594 * Fetch the element to display the tooltip on.
6595 * @return {Roo.Element} defaults to this.el
6597 tooltipEl : function()
6599 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6602 scrollToElement : function(e)
6604 var c = document.body;
6607 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6609 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6610 c = document.documentElement;
6613 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6619 var o = target.calcOffsetsTo(c);
6626 this.fireEvent('scrollto', this, options, e);
6628 Roo.get(c).scrollTo('top', options.value, true);
6633 * Set the HTML (text content) of the item
6634 * @param {string} html content for the nav item
6636 setHtml : function(html)
6639 this.htmlEl.dom.innerHTML = html;
6651 * <span> icon </span>
6652 * <span> text </span>
6653 * <span>badge </span>
6657 * @class Roo.bootstrap.NavSidebarItem
6658 * @extends Roo.bootstrap.NavItem
6659 * Bootstrap Navbar.NavSidebarItem class
6660 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6661 * {Boolean} open is the menu open
6662 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6663 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6664 * {String} buttonSize (sm|md|lg)the extra classes for the button
6665 * {Boolean} showArrow show arrow next to the text (default true)
6667 * Create a new Navbar Button
6668 * @param {Object} config The config object
6670 Roo.bootstrap.NavSidebarItem = function(config){
6671 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6676 * The raw click event for the entire grid.
6677 * @param {Roo.EventObject} e
6682 * Fires when the active item active state changes
6683 * @param {Roo.bootstrap.NavSidebarItem} this
6684 * @param {boolean} state the new state
6692 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6694 badgeWeight : 'default',
6700 buttonWeight : 'default',
6706 getAutoCreate : function(){
6711 href : this.href || '#',
6717 if(this.buttonView){
6720 href : this.href || '#',
6721 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6734 cfg.cls += ' active';
6737 if (this.disabled) {
6738 cfg.cls += ' disabled';
6741 cfg.cls += ' open x-open';
6744 if (this.glyphicon || this.icon) {
6745 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6746 a.cn.push({ tag : 'i', cls : c }) ;
6749 if(!this.buttonView){
6752 html : this.html || ''
6759 if (this.badge !== '') {
6760 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6766 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6769 a.cls += ' dropdown-toggle treeview' ;
6775 initEvents : function()
6777 if (typeof (this.menu) != 'undefined') {
6778 this.menu.parentType = this.xtype;
6779 this.menu.triggerEl = this.el;
6780 this.menu = this.addxtype(Roo.apply({}, this.menu));
6783 this.el.on('click', this.onClick, this);
6785 if(this.badge !== ''){
6786 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6791 onClick : function(e)
6798 if(this.preventDefault){
6802 this.fireEvent('click', this, e);
6805 disable : function()
6807 this.setDisabled(true);
6812 this.setDisabled(false);
6815 setDisabled : function(state)
6817 if(this.disabled == state){
6821 this.disabled = state;
6824 this.el.addClass('disabled');
6828 this.el.removeClass('disabled');
6833 setActive : function(state)
6835 if(this.active == state){
6839 this.active = state;
6842 this.el.addClass('active');
6846 this.el.removeClass('active');
6851 isActive: function ()
6856 setBadge : function(str)
6862 this.badgeEl.dom.innerHTML = str;
6877 Roo.namespace('Roo.bootstrap.breadcrumb');
6881 * @class Roo.bootstrap.breadcrumb.Nav
6882 * @extends Roo.bootstrap.Component
6883 * Bootstrap Breadcrumb Nav Class
6885 * @children Roo.bootstrap.breadcrumb.Item
6888 * Create a new breadcrumb.Nav
6889 * @param {Object} config The config object
6893 Roo.bootstrap.breadcrumb.Nav = function(config){
6894 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6899 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6901 getAutoCreate : function()
6918 initEvents: function()
6920 this.olEl = this.el.select('ol',true).first();
6922 getChildContainer : function()
6938 * @class Roo.bootstrap.breadcrumb.Nav
6939 * @extends Roo.bootstrap.Component
6940 * Bootstrap Breadcrumb Nav Class
6942 * @children Roo.bootstrap.breadcrumb.Component
6943 * @cfg {String} html the content of the link.
6944 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6945 * @cfg {Boolean} active is it active
6949 * Create a new breadcrumb.Nav
6950 * @param {Object} config The config object
6953 Roo.bootstrap.breadcrumb.Item = function(config){
6954 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6959 * The img click event for the img.
6960 * @param {Roo.EventObject} e
6967 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6972 getAutoCreate : function()
6977 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6979 if (this.href !== false) {
6986 cfg.html = this.html;
6992 initEvents: function()
6995 this.el.select('a', true).first().on('click',this.onClick, this)
6999 onClick : function(e)
7002 this.fireEvent('click',this, e);
7015 * @class Roo.bootstrap.Row
7016 * @extends Roo.bootstrap.Component
7017 * Bootstrap Row class (contains columns...)
7021 * @param {Object} config The config object
7024 Roo.bootstrap.Row = function(config){
7025 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7028 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7030 getAutoCreate : function(){
7049 * @class Roo.bootstrap.Pagination
7050 * @extends Roo.bootstrap.Component
7051 * Bootstrap Pagination class
7052 * @cfg {String} size xs | sm | md | lg
7053 * @cfg {Boolean} inverse false | true
7056 * Create a new Pagination
7057 * @param {Object} config The config object
7060 Roo.bootstrap.Pagination = function(config){
7061 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7064 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7070 getAutoCreate : function(){
7076 cfg.cls += ' inverse';
7082 cfg.cls += " " + this.cls;
7100 * @class Roo.bootstrap.PaginationItem
7101 * @extends Roo.bootstrap.Component
7102 * Bootstrap PaginationItem class
7103 * @cfg {String} html text
7104 * @cfg {String} href the link
7105 * @cfg {Boolean} preventDefault (true | false) default true
7106 * @cfg {Boolean} active (true | false) default false
7107 * @cfg {Boolean} disabled default false
7111 * Create a new PaginationItem
7112 * @param {Object} config The config object
7116 Roo.bootstrap.PaginationItem = function(config){
7117 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7122 * The raw click event for the entire grid.
7123 * @param {Roo.EventObject} e
7129 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7133 preventDefault: true,
7138 getAutoCreate : function(){
7144 href : this.href ? this.href : '#',
7145 html : this.html ? this.html : ''
7155 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7159 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7165 initEvents: function() {
7167 this.el.on('click', this.onClick, this);
7170 onClick : function(e)
7172 Roo.log('PaginationItem on click ');
7173 if(this.preventDefault){
7181 this.fireEvent('click', this, e);
7197 * @class Roo.bootstrap.Slider
7198 * @extends Roo.bootstrap.Component
7199 * Bootstrap Slider class
7202 * Create a new Slider
7203 * @param {Object} config The config object
7206 Roo.bootstrap.Slider = function(config){
7207 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7210 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7212 getAutoCreate : function(){
7216 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7220 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7232 * Ext JS Library 1.1.1
7233 * Copyright(c) 2006-2007, Ext JS, LLC.
7235 * Originally Released Under LGPL - original licence link has changed is not relivant.
7238 * <script type="text/javascript">
7243 * @class Roo.grid.ColumnModel
7244 * @extends Roo.util.Observable
7245 * This is the default implementation of a ColumnModel used by the Grid. It defines
7246 * the columns in the grid.
7249 var colModel = new Roo.grid.ColumnModel([
7250 {header: "Ticker", width: 60, sortable: true, locked: true},
7251 {header: "Company Name", width: 150, sortable: true},
7252 {header: "Market Cap.", width: 100, sortable: true},
7253 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7254 {header: "Employees", width: 100, sortable: true, resizable: false}
7259 * The config options listed for this class are options which may appear in each
7260 * individual column definition.
7261 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7263 * @param {Object} config An Array of column config objects. See this class's
7264 * config objects for details.
7266 Roo.grid.ColumnModel = function(config){
7268 * The config passed into the constructor
7270 this.config = config;
7273 // if no id, create one
7274 // if the column does not have a dataIndex mapping,
7275 // map it to the order it is in the config
7276 for(var i = 0, len = config.length; i < len; i++){
7278 if(typeof c.dataIndex == "undefined"){
7281 if(typeof c.renderer == "string"){
7282 c.renderer = Roo.util.Format[c.renderer];
7284 if(typeof c.id == "undefined"){
7287 if(c.editor && c.editor.xtype){
7288 c.editor = Roo.factory(c.editor, Roo.grid);
7290 if(c.editor && c.editor.isFormField){
7291 c.editor = new Roo.grid.GridEditor(c.editor);
7293 this.lookup[c.id] = c;
7297 * The width of columns which have no width specified (defaults to 100)
7300 this.defaultWidth = 100;
7303 * Default sortable of columns which have no sortable specified (defaults to false)
7306 this.defaultSortable = false;
7310 * @event widthchange
7311 * Fires when the width of a column changes.
7312 * @param {ColumnModel} this
7313 * @param {Number} columnIndex The column index
7314 * @param {Number} newWidth The new width
7316 "widthchange": true,
7318 * @event headerchange
7319 * Fires when the text of a header changes.
7320 * @param {ColumnModel} this
7321 * @param {Number} columnIndex The column index
7322 * @param {Number} newText The new header text
7324 "headerchange": true,
7326 * @event hiddenchange
7327 * Fires when a column is hidden or "unhidden".
7328 * @param {ColumnModel} this
7329 * @param {Number} columnIndex The column index
7330 * @param {Boolean} hidden true if hidden, false otherwise
7332 "hiddenchange": true,
7334 * @event columnmoved
7335 * Fires when a column is moved.
7336 * @param {ColumnModel} this
7337 * @param {Number} oldIndex
7338 * @param {Number} newIndex
7340 "columnmoved" : true,
7342 * @event columlockchange
7343 * Fires when a column's locked state is changed
7344 * @param {ColumnModel} this
7345 * @param {Number} colIndex
7346 * @param {Boolean} locked true if locked
7348 "columnlockchange" : true
7350 Roo.grid.ColumnModel.superclass.constructor.call(this);
7352 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7354 * @cfg {String} header The header text to display in the Grid view.
7357 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7358 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7359 * specified, the column's index is used as an index into the Record's data Array.
7362 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7363 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7366 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7367 * Defaults to the value of the {@link #defaultSortable} property.
7368 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7371 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7374 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7377 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7380 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7383 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7384 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7385 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7386 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7389 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7392 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7395 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7398 * @cfg {String} cursor (Optional)
7401 * @cfg {String} tooltip (Optional)
7404 * @cfg {Number} xs (Optional)
7407 * @cfg {Number} sm (Optional)
7410 * @cfg {Number} md (Optional)
7413 * @cfg {Number} lg (Optional)
7416 * Returns the id of the column at the specified index.
7417 * @param {Number} index The column index
7418 * @return {String} the id
7420 getColumnId : function(index){
7421 return this.config[index].id;
7425 * Returns the column for a specified id.
7426 * @param {String} id The column id
7427 * @return {Object} the column
7429 getColumnById : function(id){
7430 return this.lookup[id];
7435 * Returns the column for a specified dataIndex.
7436 * @param {String} dataIndex The column dataIndex
7437 * @return {Object|Boolean} the column or false if not found
7439 getColumnByDataIndex: function(dataIndex){
7440 var index = this.findColumnIndex(dataIndex);
7441 return index > -1 ? this.config[index] : false;
7445 * Returns the index for a specified column id.
7446 * @param {String} id The column id
7447 * @return {Number} the index, or -1 if not found
7449 getIndexById : function(id){
7450 for(var i = 0, len = this.config.length; i < len; i++){
7451 if(this.config[i].id == id){
7459 * Returns the index for a specified column dataIndex.
7460 * @param {String} dataIndex The column dataIndex
7461 * @return {Number} the index, or -1 if not found
7464 findColumnIndex : function(dataIndex){
7465 for(var i = 0, len = this.config.length; i < len; i++){
7466 if(this.config[i].dataIndex == dataIndex){
7474 moveColumn : function(oldIndex, newIndex){
7475 var c = this.config[oldIndex];
7476 this.config.splice(oldIndex, 1);
7477 this.config.splice(newIndex, 0, c);
7478 this.dataMap = null;
7479 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7482 isLocked : function(colIndex){
7483 return this.config[colIndex].locked === true;
7486 setLocked : function(colIndex, value, suppressEvent){
7487 if(this.isLocked(colIndex) == value){
7490 this.config[colIndex].locked = value;
7492 this.fireEvent("columnlockchange", this, colIndex, value);
7496 getTotalLockedWidth : function(){
7498 for(var i = 0; i < this.config.length; i++){
7499 if(this.isLocked(i) && !this.isHidden(i)){
7500 this.totalWidth += this.getColumnWidth(i);
7506 getLockedCount : function(){
7507 for(var i = 0, len = this.config.length; i < len; i++){
7508 if(!this.isLocked(i)){
7513 return this.config.length;
7517 * Returns the number of columns.
7520 getColumnCount : function(visibleOnly){
7521 if(visibleOnly === true){
7523 for(var i = 0, len = this.config.length; i < len; i++){
7524 if(!this.isHidden(i)){
7530 return this.config.length;
7534 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7535 * @param {Function} fn
7536 * @param {Object} scope (optional)
7537 * @return {Array} result
7539 getColumnsBy : function(fn, scope){
7541 for(var i = 0, len = this.config.length; i < len; i++){
7542 var c = this.config[i];
7543 if(fn.call(scope||this, c, i) === true){
7551 * Returns true if the specified column is sortable.
7552 * @param {Number} col The column index
7555 isSortable : function(col){
7556 if(typeof this.config[col].sortable == "undefined"){
7557 return this.defaultSortable;
7559 return this.config[col].sortable;
7563 * Returns the rendering (formatting) function defined for the column.
7564 * @param {Number} col The column index.
7565 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7567 getRenderer : function(col){
7568 if(!this.config[col].renderer){
7569 return Roo.grid.ColumnModel.defaultRenderer;
7571 return this.config[col].renderer;
7575 * Sets the rendering (formatting) function for a column.
7576 * @param {Number} col The column index
7577 * @param {Function} fn The function to use to process the cell's raw data
7578 * to return HTML markup for the grid view. The render function is called with
7579 * the following parameters:<ul>
7580 * <li>Data value.</li>
7581 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7582 * <li>css A CSS style string to apply to the table cell.</li>
7583 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7584 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7585 * <li>Row index</li>
7586 * <li>Column index</li>
7587 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7589 setRenderer : function(col, fn){
7590 this.config[col].renderer = fn;
7594 * Returns the width for the specified column.
7595 * @param {Number} col The column index
7598 getColumnWidth : function(col){
7599 return this.config[col].width * 1 || this.defaultWidth;
7603 * Sets the width for a column.
7604 * @param {Number} col The column index
7605 * @param {Number} width The new width
7607 setColumnWidth : function(col, width, suppressEvent){
7608 this.config[col].width = width;
7609 this.totalWidth = null;
7611 this.fireEvent("widthchange", this, col, width);
7616 * Returns the total width of all columns.
7617 * @param {Boolean} includeHidden True to include hidden column widths
7620 getTotalWidth : function(includeHidden){
7621 if(!this.totalWidth){
7622 this.totalWidth = 0;
7623 for(var i = 0, len = this.config.length; i < len; i++){
7624 if(includeHidden || !this.isHidden(i)){
7625 this.totalWidth += this.getColumnWidth(i);
7629 return this.totalWidth;
7633 * Returns the header for the specified column.
7634 * @param {Number} col The column index
7637 getColumnHeader : function(col){
7638 return this.config[col].header;
7642 * Sets the header for a column.
7643 * @param {Number} col The column index
7644 * @param {String} header The new header
7646 setColumnHeader : function(col, header){
7647 this.config[col].header = header;
7648 this.fireEvent("headerchange", this, col, header);
7652 * Returns the tooltip for the specified column.
7653 * @param {Number} col The column index
7656 getColumnTooltip : function(col){
7657 return this.config[col].tooltip;
7660 * Sets the tooltip for a column.
7661 * @param {Number} col The column index
7662 * @param {String} tooltip The new tooltip
7664 setColumnTooltip : function(col, tooltip){
7665 this.config[col].tooltip = tooltip;
7669 * Returns the dataIndex for the specified column.
7670 * @param {Number} col The column index
7673 getDataIndex : function(col){
7674 return this.config[col].dataIndex;
7678 * Sets the dataIndex for a column.
7679 * @param {Number} col The column index
7680 * @param {Number} dataIndex The new dataIndex
7682 setDataIndex : function(col, dataIndex){
7683 this.config[col].dataIndex = dataIndex;
7689 * Returns true if the cell is editable.
7690 * @param {Number} colIndex The column index
7691 * @param {Number} rowIndex The row index - this is nto actually used..?
7694 isCellEditable : function(colIndex, rowIndex){
7695 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7699 * Returns the editor defined for the cell/column.
7700 * return false or null to disable editing.
7701 * @param {Number} colIndex The column index
7702 * @param {Number} rowIndex The row index
7705 getCellEditor : function(colIndex, rowIndex){
7706 return this.config[colIndex].editor;
7710 * Sets if a column is editable.
7711 * @param {Number} col The column index
7712 * @param {Boolean} editable True if the column is editable
7714 setEditable : function(col, editable){
7715 this.config[col].editable = editable;
7720 * Returns true if the column is hidden.
7721 * @param {Number} colIndex The column index
7724 isHidden : function(colIndex){
7725 return this.config[colIndex].hidden;
7730 * Returns true if the column width cannot be changed
7732 isFixed : function(colIndex){
7733 return this.config[colIndex].fixed;
7737 * Returns true if the column can be resized
7740 isResizable : function(colIndex){
7741 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7744 * Sets if a column is hidden.
7745 * @param {Number} colIndex The column index
7746 * @param {Boolean} hidden True if the column is hidden
7748 setHidden : function(colIndex, hidden){
7749 this.config[colIndex].hidden = hidden;
7750 this.totalWidth = null;
7751 this.fireEvent("hiddenchange", this, colIndex, hidden);
7755 * Sets the editor for a column.
7756 * @param {Number} col The column index
7757 * @param {Object} editor The editor object
7759 setEditor : function(col, editor){
7760 this.config[col].editor = editor;
7764 Roo.grid.ColumnModel.defaultRenderer = function(value)
7766 if(typeof value == "object") {
7769 if(typeof value == "string" && value.length < 1){
7773 return String.format("{0}", value);
7776 // Alias for backwards compatibility
7777 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7780 * Ext JS Library 1.1.1
7781 * Copyright(c) 2006-2007, Ext JS, LLC.
7783 * Originally Released Under LGPL - original licence link has changed is not relivant.
7786 * <script type="text/javascript">
7790 * @class Roo.LoadMask
7791 * A simple utility class for generically masking elements while loading data. If the element being masked has
7792 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7793 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7794 * element's UpdateManager load indicator and will be destroyed after the initial load.
7796 * Create a new LoadMask
7797 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7798 * @param {Object} config The config object
7800 Roo.LoadMask = function(el, config){
7801 this.el = Roo.get(el);
7802 Roo.apply(this, config);
7804 this.store.on('beforeload', this.onBeforeLoad, this);
7805 this.store.on('load', this.onLoad, this);
7806 this.store.on('loadexception', this.onLoadException, this);
7807 this.removeMask = false;
7809 var um = this.el.getUpdateManager();
7810 um.showLoadIndicator = false; // disable the default indicator
7811 um.on('beforeupdate', this.onBeforeLoad, this);
7812 um.on('update', this.onLoad, this);
7813 um.on('failure', this.onLoad, this);
7814 this.removeMask = true;
7818 Roo.LoadMask.prototype = {
7820 * @cfg {Boolean} removeMask
7821 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7822 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7826 * The text to display in a centered loading message box (defaults to 'Loading...')
7830 * @cfg {String} msgCls
7831 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7833 msgCls : 'x-mask-loading',
7836 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7842 * Disables the mask to prevent it from being displayed
7844 disable : function(){
7845 this.disabled = true;
7849 * Enables the mask so that it can be displayed
7851 enable : function(){
7852 this.disabled = false;
7855 onLoadException : function()
7859 if (typeof(arguments[3]) != 'undefined') {
7860 Roo.MessageBox.alert("Error loading",arguments[3]);
7864 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7865 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7872 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7877 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7881 onBeforeLoad : function(){
7883 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7888 destroy : function(){
7890 this.store.un('beforeload', this.onBeforeLoad, this);
7891 this.store.un('load', this.onLoad, this);
7892 this.store.un('loadexception', this.onLoadException, this);
7894 var um = this.el.getUpdateManager();
7895 um.un('beforeupdate', this.onBeforeLoad, this);
7896 um.un('update', this.onLoad, this);
7897 um.un('failure', this.onLoad, this);
7908 * @class Roo.bootstrap.Table
7909 * @extends Roo.bootstrap.Component
7910 * Bootstrap Table class
7911 * @cfg {String} cls table class
7912 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7913 * @cfg {String} bgcolor Specifies the background color for a table
7914 * @cfg {Number} border Specifies whether the table cells should have borders or not
7915 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7916 * @cfg {Number} cellspacing Specifies the space between cells
7917 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7918 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7919 * @cfg {String} sortable Specifies that the table should be sortable
7920 * @cfg {String} summary Specifies a summary of the content of a table
7921 * @cfg {Number} width Specifies the width of a table
7922 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7924 * @cfg {boolean} striped Should the rows be alternative striped
7925 * @cfg {boolean} bordered Add borders to the table
7926 * @cfg {boolean} hover Add hover highlighting
7927 * @cfg {boolean} condensed Format condensed
7928 * @cfg {boolean} responsive Format condensed
7929 * @cfg {Boolean} loadMask (true|false) default false
7930 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7931 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7932 * @cfg {Boolean} rowSelection (true|false) default false
7933 * @cfg {Boolean} cellSelection (true|false) default false
7934 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7935 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7936 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7937 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7941 * Create a new Table
7942 * @param {Object} config The config object
7945 Roo.bootstrap.Table = function(config){
7946 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7951 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7952 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7953 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7954 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7956 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7958 this.sm.grid = this;
7959 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7960 this.sm = this.selModel;
7961 this.sm.xmodule = this.xmodule || false;
7964 if (this.cm && typeof(this.cm.config) == 'undefined') {
7965 this.colModel = new Roo.grid.ColumnModel(this.cm);
7966 this.cm = this.colModel;
7967 this.cm.xmodule = this.xmodule || false;
7970 this.store= Roo.factory(this.store, Roo.data);
7971 this.ds = this.store;
7972 this.ds.xmodule = this.xmodule || false;
7975 if (this.footer && this.store) {
7976 this.footer.dataSource = this.ds;
7977 this.footer = Roo.factory(this.footer);
7984 * Fires when a cell is clicked
7985 * @param {Roo.bootstrap.Table} this
7986 * @param {Roo.Element} el
7987 * @param {Number} rowIndex
7988 * @param {Number} columnIndex
7989 * @param {Roo.EventObject} e
7993 * @event celldblclick
7994 * Fires when a cell is double clicked
7995 * @param {Roo.bootstrap.Table} this
7996 * @param {Roo.Element} el
7997 * @param {Number} rowIndex
7998 * @param {Number} columnIndex
7999 * @param {Roo.EventObject} e
8001 "celldblclick" : true,
8004 * Fires when a row is clicked
8005 * @param {Roo.bootstrap.Table} this
8006 * @param {Roo.Element} el
8007 * @param {Number} rowIndex
8008 * @param {Roo.EventObject} e
8012 * @event rowdblclick
8013 * Fires when a row is double clicked
8014 * @param {Roo.bootstrap.Table} this
8015 * @param {Roo.Element} el
8016 * @param {Number} rowIndex
8017 * @param {Roo.EventObject} e
8019 "rowdblclick" : true,
8022 * Fires when a mouseover occur
8023 * @param {Roo.bootstrap.Table} this
8024 * @param {Roo.Element} el
8025 * @param {Number} rowIndex
8026 * @param {Number} columnIndex
8027 * @param {Roo.EventObject} e
8032 * Fires when a mouseout occur
8033 * @param {Roo.bootstrap.Table} this
8034 * @param {Roo.Element} el
8035 * @param {Number} rowIndex
8036 * @param {Number} columnIndex
8037 * @param {Roo.EventObject} e
8042 * Fires when a row is rendered, so you can change add a style to it.
8043 * @param {Roo.bootstrap.Table} this
8044 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8048 * @event rowsrendered
8049 * Fires when all the rows have been rendered
8050 * @param {Roo.bootstrap.Table} this
8052 'rowsrendered' : true,
8054 * @event contextmenu
8055 * The raw contextmenu event for the entire grid.
8056 * @param {Roo.EventObject} e
8058 "contextmenu" : true,
8060 * @event rowcontextmenu
8061 * Fires when a row is right clicked
8062 * @param {Roo.bootstrap.Table} this
8063 * @param {Number} rowIndex
8064 * @param {Roo.EventObject} e
8066 "rowcontextmenu" : true,
8068 * @event cellcontextmenu
8069 * Fires when a cell is right clicked
8070 * @param {Roo.bootstrap.Table} this
8071 * @param {Number} rowIndex
8072 * @param {Number} cellIndex
8073 * @param {Roo.EventObject} e
8075 "cellcontextmenu" : true,
8077 * @event headercontextmenu
8078 * Fires when a header is right clicked
8079 * @param {Roo.bootstrap.Table} this
8080 * @param {Number} columnIndex
8081 * @param {Roo.EventObject} e
8083 "headercontextmenu" : true
8087 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8113 rowSelection : false,
8114 cellSelection : false,
8117 // Roo.Element - the tbody
8119 // Roo.Element - thead element
8122 container: false, // used by gridpanel...
8128 auto_hide_footer : false,
8130 getAutoCreate : function()
8132 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8139 if (this.scrollBody) {
8140 cfg.cls += ' table-body-fixed';
8143 cfg.cls += ' table-striped';
8147 cfg.cls += ' table-hover';
8149 if (this.bordered) {
8150 cfg.cls += ' table-bordered';
8152 if (this.condensed) {
8153 cfg.cls += ' table-condensed';
8155 if (this.responsive) {
8156 cfg.cls += ' table-responsive';
8160 cfg.cls+= ' ' +this.cls;
8163 // this lot should be simplifed...
8176 ].forEach(function(k) {
8184 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8187 if(this.store || this.cm){
8188 if(this.headerShow){
8189 cfg.cn.push(this.renderHeader());
8192 cfg.cn.push(this.renderBody());
8194 if(this.footerShow){
8195 cfg.cn.push(this.renderFooter());
8197 // where does this come from?
8198 //cfg.cls+= ' TableGrid';
8201 return { cn : [ cfg ] };
8204 initEvents : function()
8206 if(!this.store || !this.cm){
8209 if (this.selModel) {
8210 this.selModel.initEvents();
8214 //Roo.log('initEvents with ds!!!!');
8216 this.mainBody = this.el.select('tbody', true).first();
8217 this.mainHead = this.el.select('thead', true).first();
8218 this.mainFoot = this.el.select('tfoot', true).first();
8224 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8225 e.on('click', _this.sort, _this);
8228 this.mainBody.on("click", this.onClick, this);
8229 this.mainBody.on("dblclick", this.onDblClick, this);
8231 // why is this done????? = it breaks dialogs??
8232 //this.parent().el.setStyle('position', 'relative');
8236 this.footer.parentId = this.id;
8237 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8240 this.el.select('tfoot tr td').first().addClass('hide');
8245 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8248 this.store.on('load', this.onLoad, this);
8249 this.store.on('beforeload', this.onBeforeLoad, this);
8250 this.store.on('update', this.onUpdate, this);
8251 this.store.on('add', this.onAdd, this);
8252 this.store.on("clear", this.clear, this);
8254 this.el.on("contextmenu", this.onContextMenu, this);
8256 this.mainBody.on('scroll', this.onBodyScroll, this);
8258 this.cm.on("headerchange", this.onHeaderChange, this);
8260 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8264 onContextMenu : function(e, t)
8266 this.processEvent("contextmenu", e);
8269 processEvent : function(name, e)
8271 if (name != 'touchstart' ) {
8272 this.fireEvent(name, e);
8275 var t = e.getTarget();
8277 var cell = Roo.get(t);
8283 if(cell.findParent('tfoot', false, true)){
8287 if(cell.findParent('thead', false, true)){
8289 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8290 cell = Roo.get(t).findParent('th', false, true);
8292 Roo.log("failed to find th in thead?");
8293 Roo.log(e.getTarget());
8298 var cellIndex = cell.dom.cellIndex;
8300 var ename = name == 'touchstart' ? 'click' : name;
8301 this.fireEvent("header" + ename, this, cellIndex, e);
8306 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8307 cell = Roo.get(t).findParent('td', false, true);
8309 Roo.log("failed to find th in tbody?");
8310 Roo.log(e.getTarget());
8315 var row = cell.findParent('tr', false, true);
8316 var cellIndex = cell.dom.cellIndex;
8317 var rowIndex = row.dom.rowIndex - 1;
8321 this.fireEvent("row" + name, this, rowIndex, e);
8325 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8331 onMouseover : function(e, el)
8333 var cell = Roo.get(el);
8339 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8340 cell = cell.findParent('td', false, true);
8343 var row = cell.findParent('tr', false, true);
8344 var cellIndex = cell.dom.cellIndex;
8345 var rowIndex = row.dom.rowIndex - 1; // start from 0
8347 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8351 onMouseout : function(e, el)
8353 var cell = Roo.get(el);
8359 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8360 cell = cell.findParent('td', false, true);
8363 var row = cell.findParent('tr', false, true);
8364 var cellIndex = cell.dom.cellIndex;
8365 var rowIndex = row.dom.rowIndex - 1; // start from 0
8367 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8371 onClick : function(e, el)
8373 var cell = Roo.get(el);
8375 if(!cell || (!this.cellSelection && !this.rowSelection)){
8379 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8380 cell = cell.findParent('td', false, true);
8383 if(!cell || typeof(cell) == 'undefined'){
8387 var row = cell.findParent('tr', false, true);
8389 if(!row || typeof(row) == 'undefined'){
8393 var cellIndex = cell.dom.cellIndex;
8394 var rowIndex = this.getRowIndex(row);
8396 // why??? - should these not be based on SelectionModel?
8397 if(this.cellSelection){
8398 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8401 if(this.rowSelection){
8402 this.fireEvent('rowclick', this, row, rowIndex, e);
8408 onDblClick : function(e,el)
8410 var cell = Roo.get(el);
8412 if(!cell || (!this.cellSelection && !this.rowSelection)){
8416 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8417 cell = cell.findParent('td', false, true);
8420 if(!cell || typeof(cell) == 'undefined'){
8424 var row = cell.findParent('tr', false, true);
8426 if(!row || typeof(row) == 'undefined'){
8430 var cellIndex = cell.dom.cellIndex;
8431 var rowIndex = this.getRowIndex(row);
8433 if(this.cellSelection){
8434 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8437 if(this.rowSelection){
8438 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8442 sort : function(e,el)
8444 var col = Roo.get(el);
8446 if(!col.hasClass('sortable')){
8450 var sort = col.attr('sort');
8453 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8457 this.store.sortInfo = {field : sort, direction : dir};
8460 Roo.log("calling footer first");
8461 this.footer.onClick('first');
8464 this.store.load({ params : { start : 0 } });
8468 renderHeader : function()
8476 this.totalWidth = 0;
8478 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8480 var config = cm.config[i];
8484 cls : 'x-hcol-' + i,
8486 html: cm.getColumnHeader(i)
8491 if(typeof(config.sortable) != 'undefined' && config.sortable){
8493 c.html = '<i class="glyphicon"></i>' + c.html;
8496 // could use BS4 hidden-..-down
8498 if(typeof(config.lgHeader) != 'undefined'){
8499 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8502 if(typeof(config.mdHeader) != 'undefined'){
8503 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8506 if(typeof(config.smHeader) != 'undefined'){
8507 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8510 if(typeof(config.xsHeader) != 'undefined'){
8511 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8518 if(typeof(config.tooltip) != 'undefined'){
8519 c.tooltip = config.tooltip;
8522 if(typeof(config.colspan) != 'undefined'){
8523 c.colspan = config.colspan;
8526 if(typeof(config.hidden) != 'undefined' && config.hidden){
8527 c.style += ' display:none;';
8530 if(typeof(config.dataIndex) != 'undefined'){
8531 c.sort = config.dataIndex;
8536 if(typeof(config.align) != 'undefined' && config.align.length){
8537 c.style += ' text-align:' + config.align + ';';
8540 if(typeof(config.width) != 'undefined'){
8541 c.style += ' width:' + config.width + 'px;';
8542 this.totalWidth += config.width;
8544 this.totalWidth += 100; // assume minimum of 100 per column?
8547 if(typeof(config.cls) != 'undefined'){
8548 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8551 ['xs','sm','md','lg'].map(function(size){
8553 if(typeof(config[size]) == 'undefined'){
8557 if (!config[size]) { // 0 = hidden
8558 // BS 4 '0' is treated as hide that column and below.
8559 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8563 c.cls += ' col-' + size + '-' + config[size] + (
8564 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8576 renderBody : function()
8586 colspan : this.cm.getColumnCount()
8596 renderFooter : function()
8606 colspan : this.cm.getColumnCount()
8620 // Roo.log('ds onload');
8625 var ds = this.store;
8627 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8628 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8629 if (_this.store.sortInfo) {
8631 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8632 e.select('i', true).addClass(['glyphicon-arrow-up']);
8635 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8636 e.select('i', true).addClass(['glyphicon-arrow-down']);
8641 var tbody = this.mainBody;
8643 if(ds.getCount() > 0){
8644 ds.data.each(function(d,rowIndex){
8645 var row = this.renderRow(cm, ds, rowIndex);
8647 tbody.createChild(row);
8651 if(row.cellObjects.length){
8652 Roo.each(row.cellObjects, function(r){
8653 _this.renderCellObject(r);
8660 var tfoot = this.el.select('tfoot', true).first();
8662 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8664 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8666 var total = this.ds.getTotalCount();
8668 if(this.footer.pageSize < total){
8669 this.mainFoot.show();
8673 Roo.each(this.el.select('tbody td', true).elements, function(e){
8674 e.on('mouseover', _this.onMouseover, _this);
8677 Roo.each(this.el.select('tbody td', true).elements, function(e){
8678 e.on('mouseout', _this.onMouseout, _this);
8680 this.fireEvent('rowsrendered', this);
8686 onUpdate : function(ds,record)
8688 this.refreshRow(record);
8692 onRemove : function(ds, record, index, isUpdate){
8693 if(isUpdate !== true){
8694 this.fireEvent("beforerowremoved", this, index, record);
8696 var bt = this.mainBody.dom;
8698 var rows = this.el.select('tbody > tr', true).elements;
8700 if(typeof(rows[index]) != 'undefined'){
8701 bt.removeChild(rows[index].dom);
8704 // if(bt.rows[index]){
8705 // bt.removeChild(bt.rows[index]);
8708 if(isUpdate !== true){
8709 //this.stripeRows(index);
8710 //this.syncRowHeights(index, index);
8712 this.fireEvent("rowremoved", this, index, record);
8716 onAdd : function(ds, records, rowIndex)
8718 //Roo.log('on Add called');
8719 // - note this does not handle multiple adding very well..
8720 var bt = this.mainBody.dom;
8721 for (var i =0 ; i < records.length;i++) {
8722 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8723 //Roo.log(records[i]);
8724 //Roo.log(this.store.getAt(rowIndex+i));
8725 this.insertRow(this.store, rowIndex + i, false);
8732 refreshRow : function(record){
8733 var ds = this.store, index;
8734 if(typeof record == 'number'){
8736 record = ds.getAt(index);
8738 index = ds.indexOf(record);
8740 return; // should not happen - but seems to
8743 this.insertRow(ds, index, true);
8745 this.onRemove(ds, record, index+1, true);
8747 //this.syncRowHeights(index, index);
8749 this.fireEvent("rowupdated", this, index, record);
8752 insertRow : function(dm, rowIndex, isUpdate){
8755 this.fireEvent("beforerowsinserted", this, rowIndex);
8757 //var s = this.getScrollState();
8758 var row = this.renderRow(this.cm, this.store, rowIndex);
8759 // insert before rowIndex..
8760 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8764 if(row.cellObjects.length){
8765 Roo.each(row.cellObjects, function(r){
8766 _this.renderCellObject(r);
8771 this.fireEvent("rowsinserted", this, rowIndex);
8772 //this.syncRowHeights(firstRow, lastRow);
8773 //this.stripeRows(firstRow);
8780 getRowDom : function(rowIndex)
8782 var rows = this.el.select('tbody > tr', true).elements;
8784 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8787 // returns the object tree for a tr..
8790 renderRow : function(cm, ds, rowIndex)
8792 var d = ds.getAt(rowIndex);
8796 cls : 'x-row-' + rowIndex,
8800 var cellObjects = [];
8802 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8803 var config = cm.config[i];
8805 var renderer = cm.getRenderer(i);
8809 if(typeof(renderer) !== 'undefined'){
8810 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8812 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8813 // and are rendered into the cells after the row is rendered - using the id for the element.
8815 if(typeof(value) === 'object'){
8825 rowIndex : rowIndex,
8830 this.fireEvent('rowclass', this, rowcfg);
8834 cls : rowcfg.rowClass + ' x-col-' + i,
8836 html: (typeof(value) === 'object') ? '' : value
8843 if(typeof(config.colspan) != 'undefined'){
8844 td.colspan = config.colspan;
8847 if(typeof(config.hidden) != 'undefined' && config.hidden){
8848 td.style += ' display:none;';
8851 if(typeof(config.align) != 'undefined' && config.align.length){
8852 td.style += ' text-align:' + config.align + ';';
8854 if(typeof(config.valign) != 'undefined' && config.valign.length){
8855 td.style += ' vertical-align:' + config.valign + ';';
8858 if(typeof(config.width) != 'undefined'){
8859 td.style += ' width:' + config.width + 'px;';
8862 if(typeof(config.cursor) != 'undefined'){
8863 td.style += ' cursor:' + config.cursor + ';';
8866 if(typeof(config.cls) != 'undefined'){
8867 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8870 ['xs','sm','md','lg'].map(function(size){
8872 if(typeof(config[size]) == 'undefined'){
8878 if (!config[size]) { // 0 = hidden
8879 // BS 4 '0' is treated as hide that column and below.
8880 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8884 td.cls += ' col-' + size + '-' + config[size] + (
8885 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8895 row.cellObjects = cellObjects;
8903 onBeforeLoad : function()
8912 this.el.select('tbody', true).first().dom.innerHTML = '';
8915 * Show or hide a row.
8916 * @param {Number} rowIndex to show or hide
8917 * @param {Boolean} state hide
8919 setRowVisibility : function(rowIndex, state)
8921 var bt = this.mainBody.dom;
8923 var rows = this.el.select('tbody > tr', true).elements;
8925 if(typeof(rows[rowIndex]) == 'undefined'){
8928 rows[rowIndex].dom.style.display = state ? '' : 'none';
8932 getSelectionModel : function(){
8934 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8936 return this.selModel;
8939 * Render the Roo.bootstrap object from renderder
8941 renderCellObject : function(r)
8945 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8947 var t = r.cfg.render(r.container);
8950 Roo.each(r.cfg.cn, function(c){
8952 container: t.getChildContainer(),
8955 _this.renderCellObject(child);
8960 getRowIndex : function(row)
8964 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8975 * Returns the grid's underlying element = used by panel.Grid
8976 * @return {Element} The element
8978 getGridEl : function(){
8982 * Forces a resize - used by panel.Grid
8983 * @return {Element} The element
8985 autoSize : function()
8987 //var ctr = Roo.get(this.container.dom.parentElement);
8988 var ctr = Roo.get(this.el.dom);
8990 var thd = this.getGridEl().select('thead',true).first();
8991 var tbd = this.getGridEl().select('tbody', true).first();
8992 var tfd = this.getGridEl().select('tfoot', true).first();
8994 var cw = ctr.getWidth();
8995 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8999 tbd.setWidth(ctr.getWidth());
9000 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9001 // this needs fixing for various usage - currently only hydra job advers I think..
9003 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9005 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9008 cw = Math.max(cw, this.totalWidth);
9009 this.getGridEl().select('tbody tr',true).setWidth(cw);
9011 // resize 'expandable coloumn?
9013 return; // we doe not have a view in this design..
9016 onBodyScroll: function()
9018 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9020 this.mainHead.setStyle({
9021 'position' : 'relative',
9022 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9028 var scrollHeight = this.mainBody.dom.scrollHeight;
9030 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9032 var height = this.mainBody.getHeight();
9034 if(scrollHeight - height == scrollTop) {
9036 var total = this.ds.getTotalCount();
9038 if(this.footer.cursor + this.footer.pageSize < total){
9040 this.footer.ds.load({
9042 start : this.footer.cursor + this.footer.pageSize,
9043 limit : this.footer.pageSize
9053 onHeaderChange : function()
9055 var header = this.renderHeader();
9056 var table = this.el.select('table', true).first();
9058 this.mainHead.remove();
9059 this.mainHead = table.createChild(header, this.mainBody, false);
9062 onHiddenChange : function(colModel, colIndex, hidden)
9064 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9065 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9067 this.CSS.updateRule(thSelector, "display", "");
9068 this.CSS.updateRule(tdSelector, "display", "");
9071 this.CSS.updateRule(thSelector, "display", "none");
9072 this.CSS.updateRule(tdSelector, "display", "none");
9075 this.onHeaderChange();
9079 setColumnWidth: function(col_index, width)
9081 // width = "md-2 xs-2..."
9082 if(!this.colModel.config[col_index]) {
9086 var w = width.split(" ");
9088 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9090 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9093 for(var j = 0; j < w.length; j++) {
9099 var size_cls = w[j].split("-");
9101 if(!Number.isInteger(size_cls[1] * 1)) {
9105 if(!this.colModel.config[col_index][size_cls[0]]) {
9109 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9113 h_row[0].classList.replace(
9114 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9115 "col-"+size_cls[0]+"-"+size_cls[1]
9118 for(var i = 0; i < rows.length; i++) {
9120 var size_cls = w[j].split("-");
9122 if(!Number.isInteger(size_cls[1] * 1)) {
9126 if(!this.colModel.config[col_index][size_cls[0]]) {
9130 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9134 rows[i].classList.replace(
9135 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9136 "col-"+size_cls[0]+"-"+size_cls[1]
9140 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9155 * @class Roo.bootstrap.TableCell
9156 * @extends Roo.bootstrap.Component
9157 * Bootstrap TableCell class
9158 * @cfg {String} html cell contain text
9159 * @cfg {String} cls cell class
9160 * @cfg {String} tag cell tag (td|th) default td
9161 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9162 * @cfg {String} align Aligns the content in a cell
9163 * @cfg {String} axis Categorizes cells
9164 * @cfg {String} bgcolor Specifies the background color of a cell
9165 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9166 * @cfg {Number} colspan Specifies the number of columns a cell should span
9167 * @cfg {String} headers Specifies one or more header cells a cell is related to
9168 * @cfg {Number} height Sets the height of a cell
9169 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9170 * @cfg {Number} rowspan Sets the number of rows a cell should span
9171 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9172 * @cfg {String} valign Vertical aligns the content in a cell
9173 * @cfg {Number} width Specifies the width of a cell
9176 * Create a new TableCell
9177 * @param {Object} config The config object
9180 Roo.bootstrap.TableCell = function(config){
9181 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9184 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9204 getAutoCreate : function(){
9205 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9225 cfg.align=this.align
9231 cfg.bgcolor=this.bgcolor
9234 cfg.charoff=this.charoff
9237 cfg.colspan=this.colspan
9240 cfg.headers=this.headers
9243 cfg.height=this.height
9246 cfg.nowrap=this.nowrap
9249 cfg.rowspan=this.rowspan
9252 cfg.scope=this.scope
9255 cfg.valign=this.valign
9258 cfg.width=this.width
9277 * @class Roo.bootstrap.TableRow
9278 * @extends Roo.bootstrap.Component
9279 * Bootstrap TableRow class
9280 * @cfg {String} cls row class
9281 * @cfg {String} align Aligns the content in a table row
9282 * @cfg {String} bgcolor Specifies a background color for a table row
9283 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9284 * @cfg {String} valign Vertical aligns the content in a table row
9287 * Create a new TableRow
9288 * @param {Object} config The config object
9291 Roo.bootstrap.TableRow = function(config){
9292 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9295 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9303 getAutoCreate : function(){
9304 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9314 cfg.align = this.align;
9317 cfg.bgcolor = this.bgcolor;
9320 cfg.charoff = this.charoff;
9323 cfg.valign = this.valign;
9341 * @class Roo.bootstrap.TableBody
9342 * @extends Roo.bootstrap.Component
9343 * Bootstrap TableBody class
9344 * @cfg {String} cls element class
9345 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9346 * @cfg {String} align Aligns the content inside the element
9347 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9348 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9351 * Create a new TableBody
9352 * @param {Object} config The config object
9355 Roo.bootstrap.TableBody = function(config){
9356 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9359 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9367 getAutoCreate : function(){
9368 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9382 cfg.align = this.align;
9385 cfg.charoff = this.charoff;
9388 cfg.valign = this.valign;
9395 // initEvents : function()
9402 // this.store = Roo.factory(this.store, Roo.data);
9403 // this.store.on('load', this.onLoad, this);
9405 // this.store.load();
9409 // onLoad: function ()
9411 // this.fireEvent('load', this);
9421 * Ext JS Library 1.1.1
9422 * Copyright(c) 2006-2007, Ext JS, LLC.
9424 * Originally Released Under LGPL - original licence link has changed is not relivant.
9427 * <script type="text/javascript">
9430 // as we use this in bootstrap.
9431 Roo.namespace('Roo.form');
9433 * @class Roo.form.Action
9434 * Internal Class used to handle form actions
9436 * @param {Roo.form.BasicForm} el The form element or its id
9437 * @param {Object} config Configuration options
9442 // define the action interface
9443 Roo.form.Action = function(form, options){
9445 this.options = options || {};
9448 * Client Validation Failed
9451 Roo.form.Action.CLIENT_INVALID = 'client';
9453 * Server Validation Failed
9456 Roo.form.Action.SERVER_INVALID = 'server';
9458 * Connect to Server Failed
9461 Roo.form.Action.CONNECT_FAILURE = 'connect';
9463 * Reading Data from Server Failed
9466 Roo.form.Action.LOAD_FAILURE = 'load';
9468 Roo.form.Action.prototype = {
9470 failureType : undefined,
9471 response : undefined,
9475 run : function(options){
9480 success : function(response){
9485 handleResponse : function(response){
9489 // default connection failure
9490 failure : function(response){
9492 this.response = response;
9493 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9494 this.form.afterAction(this, false);
9497 processResponse : function(response){
9498 this.response = response;
9499 if(!response.responseText){
9502 this.result = this.handleResponse(response);
9506 // utility functions used internally
9507 getUrl : function(appendParams){
9508 var url = this.options.url || this.form.url || this.form.el.dom.action;
9510 var p = this.getParams();
9512 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9518 getMethod : function(){
9519 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9522 getParams : function(){
9523 var bp = this.form.baseParams;
9524 var p = this.options.params;
9526 if(typeof p == "object"){
9527 p = Roo.urlEncode(Roo.applyIf(p, bp));
9528 }else if(typeof p == 'string' && bp){
9529 p += '&' + Roo.urlEncode(bp);
9532 p = Roo.urlEncode(bp);
9537 createCallback : function(){
9539 success: this.success,
9540 failure: this.failure,
9542 timeout: (this.form.timeout*1000),
9543 upload: this.form.fileUpload ? this.success : undefined
9548 Roo.form.Action.Submit = function(form, options){
9549 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9552 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9555 haveProgress : false,
9556 uploadComplete : false,
9558 // uploadProgress indicator.
9559 uploadProgress : function()
9561 if (!this.form.progressUrl) {
9565 if (!this.haveProgress) {
9566 Roo.MessageBox.progress("Uploading", "Uploading");
9568 if (this.uploadComplete) {
9569 Roo.MessageBox.hide();
9573 this.haveProgress = true;
9575 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9577 var c = new Roo.data.Connection();
9579 url : this.form.progressUrl,
9584 success : function(req){
9585 //console.log(data);
9589 rdata = Roo.decode(req.responseText)
9591 Roo.log("Invalid data from server..");
9595 if (!rdata || !rdata.success) {
9597 Roo.MessageBox.alert(Roo.encode(rdata));
9600 var data = rdata.data;
9602 if (this.uploadComplete) {
9603 Roo.MessageBox.hide();
9608 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9609 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9612 this.uploadProgress.defer(2000,this);
9615 failure: function(data) {
9616 Roo.log('progress url failed ');
9627 // run get Values on the form, so it syncs any secondary forms.
9628 this.form.getValues();
9630 var o = this.options;
9631 var method = this.getMethod();
9632 var isPost = method == 'POST';
9633 if(o.clientValidation === false || this.form.isValid()){
9635 if (this.form.progressUrl) {
9636 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9637 (new Date() * 1) + '' + Math.random());
9642 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9643 form:this.form.el.dom,
9644 url:this.getUrl(!isPost),
9646 params:isPost ? this.getParams() : null,
9647 isUpload: this.form.fileUpload,
9648 formData : this.form.formData
9651 this.uploadProgress();
9653 }else if (o.clientValidation !== false){ // client validation failed
9654 this.failureType = Roo.form.Action.CLIENT_INVALID;
9655 this.form.afterAction(this, false);
9659 success : function(response)
9661 this.uploadComplete= true;
9662 if (this.haveProgress) {
9663 Roo.MessageBox.hide();
9667 var result = this.processResponse(response);
9668 if(result === true || result.success){
9669 this.form.afterAction(this, true);
9673 this.form.markInvalid(result.errors);
9674 this.failureType = Roo.form.Action.SERVER_INVALID;
9676 this.form.afterAction(this, false);
9678 failure : function(response)
9680 this.uploadComplete= true;
9681 if (this.haveProgress) {
9682 Roo.MessageBox.hide();
9685 this.response = response;
9686 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9687 this.form.afterAction(this, false);
9690 handleResponse : function(response){
9691 if(this.form.errorReader){
9692 var rs = this.form.errorReader.read(response);
9695 for(var i = 0, len = rs.records.length; i < len; i++) {
9696 var r = rs.records[i];
9700 if(errors.length < 1){
9704 success : rs.success,
9710 ret = Roo.decode(response.responseText);
9714 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9724 Roo.form.Action.Load = function(form, options){
9725 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9726 this.reader = this.form.reader;
9729 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9734 Roo.Ajax.request(Roo.apply(
9735 this.createCallback(), {
9736 method:this.getMethod(),
9737 url:this.getUrl(false),
9738 params:this.getParams()
9742 success : function(response){
9744 var result = this.processResponse(response);
9745 if(result === true || !result.success || !result.data){
9746 this.failureType = Roo.form.Action.LOAD_FAILURE;
9747 this.form.afterAction(this, false);
9750 this.form.clearInvalid();
9751 this.form.setValues(result.data);
9752 this.form.afterAction(this, true);
9755 handleResponse : function(response){
9756 if(this.form.reader){
9757 var rs = this.form.reader.read(response);
9758 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9760 success : rs.success,
9764 return Roo.decode(response.responseText);
9768 Roo.form.Action.ACTION_TYPES = {
9769 'load' : Roo.form.Action.Load,
9770 'submit' : Roo.form.Action.Submit
9779 * @class Roo.bootstrap.Form
9780 * @extends Roo.bootstrap.Component
9781 * Bootstrap Form class
9782 * @cfg {String} method GET | POST (default POST)
9783 * @cfg {String} labelAlign top | left (default top)
9784 * @cfg {String} align left | right - for navbars
9785 * @cfg {Boolean} loadMask load mask when submit (default true)
9790 * @param {Object} config The config object
9794 Roo.bootstrap.Form = function(config){
9796 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9798 Roo.bootstrap.Form.popover.apply();
9802 * @event clientvalidation
9803 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9804 * @param {Form} this
9805 * @param {Boolean} valid true if the form has passed client-side validation
9807 clientvalidation: true,
9809 * @event beforeaction
9810 * Fires before any action is performed. Return false to cancel the action.
9811 * @param {Form} this
9812 * @param {Action} action The action to be performed
9816 * @event actionfailed
9817 * Fires when an action fails.
9818 * @param {Form} this
9819 * @param {Action} action The action that failed
9821 actionfailed : true,
9823 * @event actioncomplete
9824 * Fires when an action is completed.
9825 * @param {Form} this
9826 * @param {Action} action The action that completed
9828 actioncomplete : true
9832 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9835 * @cfg {String} method
9836 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9841 * The URL to use for form actions if one isn't supplied in the action options.
9844 * @cfg {Boolean} fileUpload
9845 * Set to true if this form is a file upload.
9849 * @cfg {Object} baseParams
9850 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9854 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9858 * @cfg {Sting} align (left|right) for navbar forms
9863 activeAction : null,
9866 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9867 * element by passing it or its id or mask the form itself by passing in true.
9870 waitMsgTarget : false,
9875 * @cfg {Boolean} errorMask (true|false) default false
9880 * @cfg {Number} maskOffset Default 100
9885 * @cfg {Boolean} maskBody
9889 getAutoCreate : function(){
9893 method : this.method || 'POST',
9894 id : this.id || Roo.id(),
9897 if (this.parent().xtype.match(/^Nav/)) {
9898 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9902 if (this.labelAlign == 'left' ) {
9903 cfg.cls += ' form-horizontal';
9909 initEvents : function()
9911 this.el.on('submit', this.onSubmit, this);
9912 // this was added as random key presses on the form where triggering form submit.
9913 this.el.on('keypress', function(e) {
9914 if (e.getCharCode() != 13) {
9917 // we might need to allow it for textareas.. and some other items.
9918 // check e.getTarget().
9920 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9924 Roo.log("keypress blocked");
9932 onSubmit : function(e){
9937 * Returns true if client-side validation on the form is successful.
9940 isValid : function(){
9941 var items = this.getItems();
9945 items.each(function(f){
9951 Roo.log('invalid field: ' + f.name);
9955 if(!target && f.el.isVisible(true)){
9961 if(this.errorMask && !valid){
9962 Roo.bootstrap.Form.popover.mask(this, target);
9969 * Returns true if any fields in this form have changed since their original load.
9972 isDirty : function(){
9974 var items = this.getItems();
9975 items.each(function(f){
9985 * Performs a predefined action (submit or load) or custom actions you define on this form.
9986 * @param {String} actionName The name of the action type
9987 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9988 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9989 * accept other config options):
9991 Property Type Description
9992 ---------------- --------------- ----------------------------------------------------------------------------------
9993 url String The url for the action (defaults to the form's url)
9994 method String The form method to use (defaults to the form's method, or POST if not defined)
9995 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9996 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9997 validate the form on the client (defaults to false)
9999 * @return {BasicForm} this
10001 doAction : function(action, options){
10002 if(typeof action == 'string'){
10003 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10005 if(this.fireEvent('beforeaction', this, action) !== false){
10006 this.beforeAction(action);
10007 action.run.defer(100, action);
10013 beforeAction : function(action){
10014 var o = action.options;
10019 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10021 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10024 // not really supported yet.. ??
10026 //if(this.waitMsgTarget === true){
10027 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10028 //}else if(this.waitMsgTarget){
10029 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10030 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10032 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10038 afterAction : function(action, success){
10039 this.activeAction = null;
10040 var o = action.options;
10045 Roo.get(document.body).unmask();
10051 //if(this.waitMsgTarget === true){
10052 // this.el.unmask();
10053 //}else if(this.waitMsgTarget){
10054 // this.waitMsgTarget.unmask();
10056 // Roo.MessageBox.updateProgress(1);
10057 // Roo.MessageBox.hide();
10064 Roo.callback(o.success, o.scope, [this, action]);
10065 this.fireEvent('actioncomplete', this, action);
10069 // failure condition..
10070 // we have a scenario where updates need confirming.
10071 // eg. if a locking scenario exists..
10072 // we look for { errors : { needs_confirm : true }} in the response.
10074 (typeof(action.result) != 'undefined') &&
10075 (typeof(action.result.errors) != 'undefined') &&
10076 (typeof(action.result.errors.needs_confirm) != 'undefined')
10079 Roo.log("not supported yet");
10082 Roo.MessageBox.confirm(
10083 "Change requires confirmation",
10084 action.result.errorMsg,
10089 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10099 Roo.callback(o.failure, o.scope, [this, action]);
10100 // show an error message if no failed handler is set..
10101 if (!this.hasListener('actionfailed')) {
10102 Roo.log("need to add dialog support");
10104 Roo.MessageBox.alert("Error",
10105 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10106 action.result.errorMsg :
10107 "Saving Failed, please check your entries or try again"
10112 this.fireEvent('actionfailed', this, action);
10117 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10118 * @param {String} id The value to search for
10121 findField : function(id){
10122 var items = this.getItems();
10123 var field = items.get(id);
10125 items.each(function(f){
10126 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10133 return field || null;
10136 * Mark fields in this form invalid in bulk.
10137 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10138 * @return {BasicForm} this
10140 markInvalid : function(errors){
10141 if(errors instanceof Array){
10142 for(var i = 0, len = errors.length; i < len; i++){
10143 var fieldError = errors[i];
10144 var f = this.findField(fieldError.id);
10146 f.markInvalid(fieldError.msg);
10152 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10153 field.markInvalid(errors[id]);
10157 //Roo.each(this.childForms || [], function (f) {
10158 // f.markInvalid(errors);
10165 * Set values for fields in this form in bulk.
10166 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10167 * @return {BasicForm} this
10169 setValues : function(values){
10170 if(values instanceof Array){ // array of objects
10171 for(var i = 0, len = values.length; i < len; i++){
10173 var f = this.findField(v.id);
10175 f.setValue(v.value);
10176 if(this.trackResetOnLoad){
10177 f.originalValue = f.getValue();
10181 }else{ // object hash
10184 if(typeof values[id] != 'function' && (field = this.findField(id))){
10186 if (field.setFromData &&
10187 field.valueField &&
10188 field.displayField &&
10189 // combos' with local stores can
10190 // be queried via setValue()
10191 // to set their value..
10192 (field.store && !field.store.isLocal)
10196 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10197 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10198 field.setFromData(sd);
10200 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10202 field.setFromData(values);
10205 field.setValue(values[id]);
10209 if(this.trackResetOnLoad){
10210 field.originalValue = field.getValue();
10216 //Roo.each(this.childForms || [], function (f) {
10217 // f.setValues(values);
10224 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10225 * they are returned as an array.
10226 * @param {Boolean} asString
10229 getValues : function(asString){
10230 //if (this.childForms) {
10231 // copy values from the child forms
10232 // Roo.each(this.childForms, function (f) {
10233 // this.setValues(f.getValues());
10239 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10240 if(asString === true){
10243 return Roo.urlDecode(fs);
10247 * Returns the fields in this form as an object with key/value pairs.
10248 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10251 getFieldValues : function(with_hidden)
10253 var items = this.getItems();
10255 items.each(function(f){
10257 if (!f.getName()) {
10261 var v = f.getValue();
10263 if (f.inputType =='radio') {
10264 if (typeof(ret[f.getName()]) == 'undefined') {
10265 ret[f.getName()] = ''; // empty..
10268 if (!f.el.dom.checked) {
10272 v = f.el.dom.value;
10276 if(f.xtype == 'MoneyField'){
10277 ret[f.currencyName] = f.getCurrency();
10280 // not sure if this supported any more..
10281 if ((typeof(v) == 'object') && f.getRawValue) {
10282 v = f.getRawValue() ; // dates..
10284 // combo boxes where name != hiddenName...
10285 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10286 ret[f.name] = f.getRawValue();
10288 ret[f.getName()] = v;
10295 * Clears all invalid messages in this form.
10296 * @return {BasicForm} this
10298 clearInvalid : function(){
10299 var items = this.getItems();
10301 items.each(function(f){
10309 * Resets this form.
10310 * @return {BasicForm} this
10312 reset : function(){
10313 var items = this.getItems();
10314 items.each(function(f){
10318 Roo.each(this.childForms || [], function (f) {
10326 getItems : function()
10328 var r=new Roo.util.MixedCollection(false, function(o){
10329 return o.id || (o.id = Roo.id());
10331 var iter = function(el) {
10338 Roo.each(el.items,function(e) {
10347 hideFields : function(items)
10349 Roo.each(items, function(i){
10351 var f = this.findField(i);
10362 showFields : function(items)
10364 Roo.each(items, function(i){
10366 var f = this.findField(i);
10379 Roo.apply(Roo.bootstrap.Form, {
10395 intervalID : false,
10401 if(this.isApplied){
10406 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10407 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10408 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10409 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10412 this.maskEl.top.enableDisplayMode("block");
10413 this.maskEl.left.enableDisplayMode("block");
10414 this.maskEl.bottom.enableDisplayMode("block");
10415 this.maskEl.right.enableDisplayMode("block");
10417 this.toolTip = new Roo.bootstrap.Tooltip({
10418 cls : 'roo-form-error-popover',
10420 'left' : ['r-l', [-2,0], 'right'],
10421 'right' : ['l-r', [2,0], 'left'],
10422 'bottom' : ['tl-bl', [0,2], 'top'],
10423 'top' : [ 'bl-tl', [0,-2], 'bottom']
10427 this.toolTip.render(Roo.get(document.body));
10429 this.toolTip.el.enableDisplayMode("block");
10431 Roo.get(document.body).on('click', function(){
10435 Roo.get(document.body).on('touchstart', function(){
10439 this.isApplied = true
10442 mask : function(form, target)
10446 this.target = target;
10448 if(!this.form.errorMask || !target.el){
10452 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10454 Roo.log(scrollable);
10456 var ot = this.target.el.calcOffsetsTo(scrollable);
10458 var scrollTo = ot[1] - this.form.maskOffset;
10460 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10462 scrollable.scrollTo('top', scrollTo);
10464 var box = this.target.el.getBox();
10466 var zIndex = Roo.bootstrap.Modal.zIndex++;
10469 this.maskEl.top.setStyle('position', 'absolute');
10470 this.maskEl.top.setStyle('z-index', zIndex);
10471 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10472 this.maskEl.top.setLeft(0);
10473 this.maskEl.top.setTop(0);
10474 this.maskEl.top.show();
10476 this.maskEl.left.setStyle('position', 'absolute');
10477 this.maskEl.left.setStyle('z-index', zIndex);
10478 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10479 this.maskEl.left.setLeft(0);
10480 this.maskEl.left.setTop(box.y - this.padding);
10481 this.maskEl.left.show();
10483 this.maskEl.bottom.setStyle('position', 'absolute');
10484 this.maskEl.bottom.setStyle('z-index', zIndex);
10485 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10486 this.maskEl.bottom.setLeft(0);
10487 this.maskEl.bottom.setTop(box.bottom + this.padding);
10488 this.maskEl.bottom.show();
10490 this.maskEl.right.setStyle('position', 'absolute');
10491 this.maskEl.right.setStyle('z-index', zIndex);
10492 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10493 this.maskEl.right.setLeft(box.right + this.padding);
10494 this.maskEl.right.setTop(box.y - this.padding);
10495 this.maskEl.right.show();
10497 this.toolTip.bindEl = this.target.el;
10499 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10501 var tip = this.target.blankText;
10503 if(this.target.getValue() !== '' ) {
10505 if (this.target.invalidText.length) {
10506 tip = this.target.invalidText;
10507 } else if (this.target.regexText.length){
10508 tip = this.target.regexText;
10512 this.toolTip.show(tip);
10514 this.intervalID = window.setInterval(function() {
10515 Roo.bootstrap.Form.popover.unmask();
10518 window.onwheel = function(){ return false;};
10520 (function(){ this.isMasked = true; }).defer(500, this);
10524 unmask : function()
10526 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10530 this.maskEl.top.setStyle('position', 'absolute');
10531 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10532 this.maskEl.top.hide();
10534 this.maskEl.left.setStyle('position', 'absolute');
10535 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10536 this.maskEl.left.hide();
10538 this.maskEl.bottom.setStyle('position', 'absolute');
10539 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10540 this.maskEl.bottom.hide();
10542 this.maskEl.right.setStyle('position', 'absolute');
10543 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10544 this.maskEl.right.hide();
10546 this.toolTip.hide();
10548 this.toolTip.el.hide();
10550 window.onwheel = function(){ return true;};
10552 if(this.intervalID){
10553 window.clearInterval(this.intervalID);
10554 this.intervalID = false;
10557 this.isMasked = false;
10567 * Ext JS Library 1.1.1
10568 * Copyright(c) 2006-2007, Ext JS, LLC.
10570 * Originally Released Under LGPL - original licence link has changed is not relivant.
10573 * <script type="text/javascript">
10576 * @class Roo.form.VTypes
10577 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10580 Roo.form.VTypes = function(){
10581 // closure these in so they are only created once.
10582 var alpha = /^[a-zA-Z_]+$/;
10583 var alphanum = /^[a-zA-Z0-9_]+$/;
10584 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10585 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10587 // All these messages and functions are configurable
10590 * The function used to validate email addresses
10591 * @param {String} value The email address
10593 'email' : function(v){
10594 return email.test(v);
10597 * The error text to display when the email validation function returns false
10600 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10602 * The keystroke filter mask to be applied on email input
10605 'emailMask' : /[a-z0-9_\.\-@]/i,
10608 * The function used to validate URLs
10609 * @param {String} value The URL
10611 'url' : function(v){
10612 return url.test(v);
10615 * The error text to display when the url validation function returns false
10618 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10621 * The function used to validate alpha values
10622 * @param {String} value The value
10624 'alpha' : function(v){
10625 return alpha.test(v);
10628 * The error text to display when the alpha validation function returns false
10631 'alphaText' : 'This field should only contain letters and _',
10633 * The keystroke filter mask to be applied on alpha input
10636 'alphaMask' : /[a-z_]/i,
10639 * The function used to validate alphanumeric values
10640 * @param {String} value The value
10642 'alphanum' : function(v){
10643 return alphanum.test(v);
10646 * The error text to display when the alphanumeric validation function returns false
10649 'alphanumText' : 'This field should only contain letters, numbers and _',
10651 * The keystroke filter mask to be applied on alphanumeric input
10654 'alphanumMask' : /[a-z0-9_]/i
10664 * @class Roo.bootstrap.Input
10665 * @extends Roo.bootstrap.Component
10666 * Bootstrap Input class
10667 * @cfg {Boolean} disabled is it disabled
10668 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10669 * @cfg {String} name name of the input
10670 * @cfg {string} fieldLabel - the label associated
10671 * @cfg {string} placeholder - placeholder to put in text.
10672 * @cfg {string} before - input group add on before
10673 * @cfg {string} after - input group add on after
10674 * @cfg {string} size - (lg|sm) or leave empty..
10675 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10676 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10677 * @cfg {Number} md colspan out of 12 for computer-sized screens
10678 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10679 * @cfg {string} value default value of the input
10680 * @cfg {Number} labelWidth set the width of label
10681 * @cfg {Number} labellg set the width of label (1-12)
10682 * @cfg {Number} labelmd set the width of label (1-12)
10683 * @cfg {Number} labelsm set the width of label (1-12)
10684 * @cfg {Number} labelxs set the width of label (1-12)
10685 * @cfg {String} labelAlign (top|left)
10686 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10687 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10688 * @cfg {String} indicatorpos (left|right) default left
10689 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10690 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10691 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10693 * @cfg {String} align (left|center|right) Default left
10694 * @cfg {Boolean} forceFeedback (true|false) Default false
10697 * Create a new Input
10698 * @param {Object} config The config object
10701 Roo.bootstrap.Input = function(config){
10703 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10708 * Fires when this field receives input focus.
10709 * @param {Roo.form.Field} this
10714 * Fires when this field loses input focus.
10715 * @param {Roo.form.Field} this
10719 * @event specialkey
10720 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10721 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10722 * @param {Roo.form.Field} this
10723 * @param {Roo.EventObject} e The event object
10728 * Fires just before the field blurs if the field value has changed.
10729 * @param {Roo.form.Field} this
10730 * @param {Mixed} newValue The new value
10731 * @param {Mixed} oldValue The original value
10736 * Fires after the field has been marked as invalid.
10737 * @param {Roo.form.Field} this
10738 * @param {String} msg The validation message
10743 * Fires after the field has been validated with no errors.
10744 * @param {Roo.form.Field} this
10749 * Fires after the key up
10750 * @param {Roo.form.Field} this
10751 * @param {Roo.EventObject} e The event Object
10756 * Fires after the user pastes into input
10757 * @param {Roo.form.Field} this
10758 * @param {Roo.EventObject} e The event Object
10764 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10766 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10767 automatic validation (defaults to "keyup").
10769 validationEvent : "keyup",
10771 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10773 validateOnBlur : true,
10775 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10777 validationDelay : 250,
10779 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10781 focusClass : "x-form-focus", // not needed???
10785 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10787 invalidClass : "has-warning",
10790 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10792 validClass : "has-success",
10795 * @cfg {Boolean} hasFeedback (true|false) default true
10797 hasFeedback : true,
10800 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10802 invalidFeedbackClass : "glyphicon-warning-sign",
10805 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10807 validFeedbackClass : "glyphicon-ok",
10810 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10812 selectOnFocus : false,
10815 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10819 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10824 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10826 disableKeyFilter : false,
10829 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10833 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10837 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10839 blankText : "Please complete this mandatory field",
10842 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10846 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10848 maxLength : Number.MAX_VALUE,
10850 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10852 minLengthText : "The minimum length for this field is {0}",
10854 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10856 maxLengthText : "The maximum length for this field is {0}",
10860 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10861 * If available, this function will be called only after the basic validators all return true, and will be passed the
10862 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10866 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10867 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10868 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10872 * @cfg {String} regexText -- Depricated - use Invalid Text
10877 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10883 autocomplete: false,
10887 inputType : 'text',
10890 placeholder: false,
10895 preventMark: false,
10896 isFormField : true,
10899 labelAlign : false,
10902 formatedValue : false,
10903 forceFeedback : false,
10905 indicatorpos : 'left',
10915 parentLabelAlign : function()
10918 while (parent.parent()) {
10919 parent = parent.parent();
10920 if (typeof(parent.labelAlign) !='undefined') {
10921 return parent.labelAlign;
10928 getAutoCreate : function()
10930 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10936 if(this.inputType != 'hidden'){
10937 cfg.cls = 'form-group' //input-group
10943 type : this.inputType,
10944 value : this.value,
10945 cls : 'form-control',
10946 placeholder : this.placeholder || '',
10947 autocomplete : this.autocomplete || 'new-password'
10949 if (this.inputType == 'file') {
10950 input.style = 'overflow:hidden'; // why not in CSS?
10953 if(this.capture.length){
10954 input.capture = this.capture;
10957 if(this.accept.length){
10958 input.accept = this.accept + "/*";
10962 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10965 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10966 input.maxLength = this.maxLength;
10969 if (this.disabled) {
10970 input.disabled=true;
10973 if (this.readOnly) {
10974 input.readonly=true;
10978 input.name = this.name;
10982 input.cls += ' input-' + this.size;
10986 ['xs','sm','md','lg'].map(function(size){
10987 if (settings[size]) {
10988 cfg.cls += ' col-' + size + '-' + settings[size];
10992 var inputblock = input;
10996 cls: 'glyphicon form-control-feedback'
10999 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11002 cls : 'has-feedback',
11010 if (this.before || this.after) {
11013 cls : 'input-group',
11017 if (this.before && typeof(this.before) == 'string') {
11019 inputblock.cn.push({
11021 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11025 if (this.before && typeof(this.before) == 'object') {
11026 this.before = Roo.factory(this.before);
11028 inputblock.cn.push({
11030 cls : 'roo-input-before input-group-prepend input-group-' +
11031 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11035 inputblock.cn.push(input);
11037 if (this.after && typeof(this.after) == 'string') {
11038 inputblock.cn.push({
11040 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11044 if (this.after && typeof(this.after) == 'object') {
11045 this.after = Roo.factory(this.after);
11047 inputblock.cn.push({
11049 cls : 'roo-input-after input-group-append input-group-' +
11050 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11054 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11055 inputblock.cls += ' has-feedback';
11056 inputblock.cn.push(feedback);
11061 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11062 tooltip : 'This field is required'
11064 if (this.allowBlank ) {
11065 indicator.style = this.allowBlank ? ' display:none' : '';
11067 if (align ==='left' && this.fieldLabel.length) {
11069 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11076 cls : 'control-label col-form-label',
11077 html : this.fieldLabel
11088 var labelCfg = cfg.cn[1];
11089 var contentCfg = cfg.cn[2];
11091 if(this.indicatorpos == 'right'){
11096 cls : 'control-label col-form-label',
11100 html : this.fieldLabel
11114 labelCfg = cfg.cn[0];
11115 contentCfg = cfg.cn[1];
11119 if(this.labelWidth > 12){
11120 labelCfg.style = "width: " + this.labelWidth + 'px';
11123 if(this.labelWidth < 13 && this.labelmd == 0){
11124 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11127 if(this.labellg > 0){
11128 labelCfg.cls += ' col-lg-' + this.labellg;
11129 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11132 if(this.labelmd > 0){
11133 labelCfg.cls += ' col-md-' + this.labelmd;
11134 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11137 if(this.labelsm > 0){
11138 labelCfg.cls += ' col-sm-' + this.labelsm;
11139 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11142 if(this.labelxs > 0){
11143 labelCfg.cls += ' col-xs-' + this.labelxs;
11144 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11148 } else if ( this.fieldLabel.length) {
11155 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11156 tooltip : 'This field is required',
11157 style : this.allowBlank ? ' display:none' : ''
11161 //cls : 'input-group-addon',
11162 html : this.fieldLabel
11170 if(this.indicatorpos == 'right'){
11175 //cls : 'input-group-addon',
11176 html : this.fieldLabel
11181 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11182 tooltip : 'This field is required',
11183 style : this.allowBlank ? ' display:none' : ''
11203 if (this.parentType === 'Navbar' && this.parent().bar) {
11204 cfg.cls += ' navbar-form';
11207 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11208 // on BS4 we do this only if not form
11209 cfg.cls += ' navbar-form';
11217 * return the real input element.
11219 inputEl: function ()
11221 return this.el.select('input.form-control',true).first();
11224 tooltipEl : function()
11226 return this.inputEl();
11229 indicatorEl : function()
11231 if (Roo.bootstrap.version == 4) {
11232 return false; // not enabled in v4 yet.
11235 var indicator = this.el.select('i.roo-required-indicator',true).first();
11245 setDisabled : function(v)
11247 var i = this.inputEl().dom;
11249 i.removeAttribute('disabled');
11253 i.setAttribute('disabled','true');
11255 initEvents : function()
11258 this.inputEl().on("keydown" , this.fireKey, this);
11259 this.inputEl().on("focus", this.onFocus, this);
11260 this.inputEl().on("blur", this.onBlur, this);
11262 this.inputEl().relayEvent('keyup', this);
11263 this.inputEl().relayEvent('paste', this);
11265 this.indicator = this.indicatorEl();
11267 if(this.indicator){
11268 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11271 // reference to original value for reset
11272 this.originalValue = this.getValue();
11273 //Roo.form.TextField.superclass.initEvents.call(this);
11274 if(this.validationEvent == 'keyup'){
11275 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11276 this.inputEl().on('keyup', this.filterValidation, this);
11278 else if(this.validationEvent !== false){
11279 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11282 if(this.selectOnFocus){
11283 this.on("focus", this.preFocus, this);
11286 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11287 this.inputEl().on("keypress", this.filterKeys, this);
11289 this.inputEl().relayEvent('keypress', this);
11292 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11293 this.el.on("click", this.autoSize, this);
11296 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11297 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11300 if (typeof(this.before) == 'object') {
11301 this.before.render(this.el.select('.roo-input-before',true).first());
11303 if (typeof(this.after) == 'object') {
11304 this.after.render(this.el.select('.roo-input-after',true).first());
11307 this.inputEl().on('change', this.onChange, this);
11310 filterValidation : function(e){
11311 if(!e.isNavKeyPress()){
11312 this.validationTask.delay(this.validationDelay);
11316 * Validates the field value
11317 * @return {Boolean} True if the value is valid, else false
11319 validate : function(){
11320 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11321 if(this.disabled || this.validateValue(this.getRawValue())){
11326 this.markInvalid();
11332 * Validates a value according to the field's validation rules and marks the field as invalid
11333 * if the validation fails
11334 * @param {Mixed} value The value to validate
11335 * @return {Boolean} True if the value is valid, else false
11337 validateValue : function(value)
11339 if(this.getVisibilityEl().hasClass('hidden')){
11343 if(value.length < 1) { // if it's blank
11344 if(this.allowBlank){
11350 if(value.length < this.minLength){
11353 if(value.length > this.maxLength){
11357 var vt = Roo.form.VTypes;
11358 if(!vt[this.vtype](value, this)){
11362 if(typeof this.validator == "function"){
11363 var msg = this.validator(value);
11367 if (typeof(msg) == 'string') {
11368 this.invalidText = msg;
11372 if(this.regex && !this.regex.test(value)){
11380 fireKey : function(e){
11381 //Roo.log('field ' + e.getKey());
11382 if(e.isNavKeyPress()){
11383 this.fireEvent("specialkey", this, e);
11386 focus : function (selectText){
11388 this.inputEl().focus();
11389 if(selectText === true){
11390 this.inputEl().dom.select();
11396 onFocus : function(){
11397 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11398 // this.el.addClass(this.focusClass);
11400 if(!this.hasFocus){
11401 this.hasFocus = true;
11402 this.startValue = this.getValue();
11403 this.fireEvent("focus", this);
11407 beforeBlur : Roo.emptyFn,
11411 onBlur : function(){
11413 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11414 //this.el.removeClass(this.focusClass);
11416 this.hasFocus = false;
11417 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11420 var v = this.getValue();
11421 if(String(v) !== String(this.startValue)){
11422 this.fireEvent('change', this, v, this.startValue);
11424 this.fireEvent("blur", this);
11427 onChange : function(e)
11429 var v = this.getValue();
11430 if(String(v) !== String(this.startValue)){
11431 this.fireEvent('change', this, v, this.startValue);
11437 * Resets the current field value to the originally loaded value and clears any validation messages
11439 reset : function(){
11440 this.setValue(this.originalValue);
11444 * Returns the name of the field
11445 * @return {Mixed} name The name field
11447 getName: function(){
11451 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11452 * @return {Mixed} value The field value
11454 getValue : function(){
11456 var v = this.inputEl().getValue();
11461 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11462 * @return {Mixed} value The field value
11464 getRawValue : function(){
11465 var v = this.inputEl().getValue();
11471 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11472 * @param {Mixed} value The value to set
11474 setRawValue : function(v){
11475 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11478 selectText : function(start, end){
11479 var v = this.getRawValue();
11481 start = start === undefined ? 0 : start;
11482 end = end === undefined ? v.length : end;
11483 var d = this.inputEl().dom;
11484 if(d.setSelectionRange){
11485 d.setSelectionRange(start, end);
11486 }else if(d.createTextRange){
11487 var range = d.createTextRange();
11488 range.moveStart("character", start);
11489 range.moveEnd("character", v.length-end);
11496 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11497 * @param {Mixed} value The value to set
11499 setValue : function(v){
11502 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11508 processValue : function(value){
11509 if(this.stripCharsRe){
11510 var newValue = value.replace(this.stripCharsRe, '');
11511 if(newValue !== value){
11512 this.setRawValue(newValue);
11519 preFocus : function(){
11521 if(this.selectOnFocus){
11522 this.inputEl().dom.select();
11525 filterKeys : function(e){
11526 var k = e.getKey();
11527 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11530 var c = e.getCharCode(), cc = String.fromCharCode(c);
11531 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11534 if(!this.maskRe.test(cc)){
11539 * Clear any invalid styles/messages for this field
11541 clearInvalid : function(){
11543 if(!this.el || this.preventMark){ // not rendered
11548 this.el.removeClass([this.invalidClass, 'is-invalid']);
11550 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11552 var feedback = this.el.select('.form-control-feedback', true).first();
11555 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11560 if(this.indicator){
11561 this.indicator.removeClass('visible');
11562 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11565 this.fireEvent('valid', this);
11569 * Mark this field as valid
11571 markValid : function()
11573 if(!this.el || this.preventMark){ // not rendered...
11577 this.el.removeClass([this.invalidClass, this.validClass]);
11578 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11580 var feedback = this.el.select('.form-control-feedback', true).first();
11583 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11586 if(this.indicator){
11587 this.indicator.removeClass('visible');
11588 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11596 if(this.allowBlank && !this.getRawValue().length){
11599 if (Roo.bootstrap.version == 3) {
11600 this.el.addClass(this.validClass);
11602 this.inputEl().addClass('is-valid');
11605 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11607 var feedback = this.el.select('.form-control-feedback', true).first();
11610 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11611 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11616 this.fireEvent('valid', this);
11620 * Mark this field as invalid
11621 * @param {String} msg The validation message
11623 markInvalid : function(msg)
11625 if(!this.el || this.preventMark){ // not rendered
11629 this.el.removeClass([this.invalidClass, this.validClass]);
11630 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11632 var feedback = this.el.select('.form-control-feedback', true).first();
11635 this.el.select('.form-control-feedback', true).first().removeClass(
11636 [this.invalidFeedbackClass, this.validFeedbackClass]);
11643 if(this.allowBlank && !this.getRawValue().length){
11647 if(this.indicator){
11648 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11649 this.indicator.addClass('visible');
11651 if (Roo.bootstrap.version == 3) {
11652 this.el.addClass(this.invalidClass);
11654 this.inputEl().addClass('is-invalid');
11659 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11661 var feedback = this.el.select('.form-control-feedback', true).first();
11664 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11666 if(this.getValue().length || this.forceFeedback){
11667 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11674 this.fireEvent('invalid', this, msg);
11677 SafariOnKeyDown : function(event)
11679 // this is a workaround for a password hang bug on chrome/ webkit.
11680 if (this.inputEl().dom.type != 'password') {
11684 var isSelectAll = false;
11686 if(this.inputEl().dom.selectionEnd > 0){
11687 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11689 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11690 event.preventDefault();
11695 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11697 event.preventDefault();
11698 // this is very hacky as keydown always get's upper case.
11700 var cc = String.fromCharCode(event.getCharCode());
11701 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11705 adjustWidth : function(tag, w){
11706 tag = tag.toLowerCase();
11707 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11708 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11709 if(tag == 'input'){
11712 if(tag == 'textarea'){
11715 }else if(Roo.isOpera){
11716 if(tag == 'input'){
11719 if(tag == 'textarea'){
11727 setFieldLabel : function(v)
11729 if(!this.rendered){
11733 if(this.indicatorEl()){
11734 var ar = this.el.select('label > span',true);
11736 if (ar.elements.length) {
11737 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11738 this.fieldLabel = v;
11742 var br = this.el.select('label',true);
11744 if(br.elements.length) {
11745 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11746 this.fieldLabel = v;
11750 Roo.log('Cannot Found any of label > span || label in input');
11754 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11755 this.fieldLabel = v;
11770 * @class Roo.bootstrap.TextArea
11771 * @extends Roo.bootstrap.Input
11772 * Bootstrap TextArea class
11773 * @cfg {Number} cols Specifies the visible width of a text area
11774 * @cfg {Number} rows Specifies the visible number of lines in a text area
11775 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11776 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11777 * @cfg {string} html text
11780 * Create a new TextArea
11781 * @param {Object} config The config object
11784 Roo.bootstrap.TextArea = function(config){
11785 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11789 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11799 getAutoCreate : function(){
11801 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11807 if(this.inputType != 'hidden'){
11808 cfg.cls = 'form-group' //input-group
11816 value : this.value || '',
11817 html: this.html || '',
11818 cls : 'form-control',
11819 placeholder : this.placeholder || ''
11823 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11824 input.maxLength = this.maxLength;
11828 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11832 input.cols = this.cols;
11835 if (this.readOnly) {
11836 input.readonly = true;
11840 input.name = this.name;
11844 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11848 ['xs','sm','md','lg'].map(function(size){
11849 if (settings[size]) {
11850 cfg.cls += ' col-' + size + '-' + settings[size];
11854 var inputblock = input;
11856 if(this.hasFeedback && !this.allowBlank){
11860 cls: 'glyphicon form-control-feedback'
11864 cls : 'has-feedback',
11873 if (this.before || this.after) {
11876 cls : 'input-group',
11880 inputblock.cn.push({
11882 cls : 'input-group-addon',
11887 inputblock.cn.push(input);
11889 if(this.hasFeedback && !this.allowBlank){
11890 inputblock.cls += ' has-feedback';
11891 inputblock.cn.push(feedback);
11895 inputblock.cn.push({
11897 cls : 'input-group-addon',
11904 if (align ==='left' && this.fieldLabel.length) {
11909 cls : 'control-label',
11910 html : this.fieldLabel
11921 if(this.labelWidth > 12){
11922 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11925 if(this.labelWidth < 13 && this.labelmd == 0){
11926 this.labelmd = this.labelWidth;
11929 if(this.labellg > 0){
11930 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11931 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11934 if(this.labelmd > 0){
11935 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11936 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11939 if(this.labelsm > 0){
11940 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11941 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11944 if(this.labelxs > 0){
11945 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11946 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11949 } else if ( this.fieldLabel.length) {
11954 //cls : 'input-group-addon',
11955 html : this.fieldLabel
11973 if (this.disabled) {
11974 input.disabled=true;
11981 * return the real textarea element.
11983 inputEl: function ()
11985 return this.el.select('textarea.form-control',true).first();
11989 * Clear any invalid styles/messages for this field
11991 clearInvalid : function()
11994 if(!this.el || this.preventMark){ // not rendered
11998 var label = this.el.select('label', true).first();
11999 var icon = this.el.select('i.fa-star', true).first();
12004 this.el.removeClass( this.validClass);
12005 this.inputEl().removeClass('is-invalid');
12007 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12009 var feedback = this.el.select('.form-control-feedback', true).first();
12012 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12017 this.fireEvent('valid', this);
12021 * Mark this field as valid
12023 markValid : function()
12025 if(!this.el || this.preventMark){ // not rendered
12029 this.el.removeClass([this.invalidClass, this.validClass]);
12030 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12032 var feedback = this.el.select('.form-control-feedback', true).first();
12035 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12038 if(this.disabled || this.allowBlank){
12042 var label = this.el.select('label', true).first();
12043 var icon = this.el.select('i.fa-star', true).first();
12048 if (Roo.bootstrap.version == 3) {
12049 this.el.addClass(this.validClass);
12051 this.inputEl().addClass('is-valid');
12055 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12057 var feedback = this.el.select('.form-control-feedback', true).first();
12060 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12061 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12066 this.fireEvent('valid', this);
12070 * Mark this field as invalid
12071 * @param {String} msg The validation message
12073 markInvalid : function(msg)
12075 if(!this.el || this.preventMark){ // not rendered
12079 this.el.removeClass([this.invalidClass, this.validClass]);
12080 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12082 var feedback = this.el.select('.form-control-feedback', true).first();
12085 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12088 if(this.disabled || this.allowBlank){
12092 var label = this.el.select('label', true).first();
12093 var icon = this.el.select('i.fa-star', true).first();
12095 if(!this.getValue().length && label && !icon){
12096 this.el.createChild({
12098 cls : 'text-danger fa fa-lg fa-star',
12099 tooltip : 'This field is required',
12100 style : 'margin-right:5px;'
12104 if (Roo.bootstrap.version == 3) {
12105 this.el.addClass(this.invalidClass);
12107 this.inputEl().addClass('is-invalid');
12110 // fixme ... this may be depricated need to test..
12111 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
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]);
12118 if(this.getValue().length || this.forceFeedback){
12119 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12126 this.fireEvent('invalid', this, msg);
12134 * trigger field - base class for combo..
12139 * @class Roo.bootstrap.TriggerField
12140 * @extends Roo.bootstrap.Input
12141 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12142 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12143 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12144 * for which you can provide a custom implementation. For example:
12146 var trigger = new Roo.bootstrap.TriggerField();
12147 trigger.onTriggerClick = myTriggerFn;
12148 trigger.applyTo('my-field');
12151 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12152 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12153 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12154 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12155 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12158 * Create a new TriggerField.
12159 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12160 * to the base TextField)
12162 Roo.bootstrap.TriggerField = function(config){
12163 this.mimicing = false;
12164 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12167 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12169 * @cfg {String} triggerClass A CSS class to apply to the trigger
12172 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12177 * @cfg {Boolean} removable (true|false) special filter default false
12181 /** @cfg {Boolean} grow @hide */
12182 /** @cfg {Number} growMin @hide */
12183 /** @cfg {Number} growMax @hide */
12189 autoSize: Roo.emptyFn,
12193 deferHeight : true,
12196 actionMode : 'wrap',
12201 getAutoCreate : function(){
12203 var align = this.labelAlign || this.parentLabelAlign();
12208 cls: 'form-group' //input-group
12215 type : this.inputType,
12216 cls : 'form-control',
12217 autocomplete: 'new-password',
12218 placeholder : this.placeholder || ''
12222 input.name = this.name;
12225 input.cls += ' input-' + this.size;
12228 if (this.disabled) {
12229 input.disabled=true;
12232 var inputblock = input;
12234 if(this.hasFeedback && !this.allowBlank){
12238 cls: 'glyphicon form-control-feedback'
12241 if(this.removable && !this.editable ){
12243 cls : 'has-feedback',
12249 cls : 'roo-combo-removable-btn close'
12256 cls : 'has-feedback',
12265 if(this.removable && !this.editable ){
12267 cls : 'roo-removable',
12273 cls : 'roo-combo-removable-btn close'
12280 if (this.before || this.after) {
12283 cls : 'input-group',
12287 inputblock.cn.push({
12289 cls : 'input-group-addon input-group-prepend input-group-text',
12294 inputblock.cn.push(input);
12296 if(this.hasFeedback && !this.allowBlank){
12297 inputblock.cls += ' has-feedback';
12298 inputblock.cn.push(feedback);
12302 inputblock.cn.push({
12304 cls : 'input-group-addon input-group-append input-group-text',
12313 var ibwrap = inputblock;
12318 cls: 'roo-select2-choices',
12322 cls: 'roo-select2-search-field',
12334 cls: 'roo-select2-container input-group',
12339 cls: 'form-hidden-field'
12345 if(!this.multiple && this.showToggleBtn){
12351 if (this.caret != false) {
12354 cls: 'fa fa-' + this.caret
12361 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12363 Roo.bootstrap.version == 3 ? caret : '',
12366 cls: 'combobox-clear',
12380 combobox.cls += ' roo-select2-container-multi';
12384 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12385 tooltip : 'This field is required'
12387 if (Roo.bootstrap.version == 4) {
12390 style : 'display:none'
12395 if (align ==='left' && this.fieldLabel.length) {
12397 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12404 cls : 'control-label',
12405 html : this.fieldLabel
12417 var labelCfg = cfg.cn[1];
12418 var contentCfg = cfg.cn[2];
12420 if(this.indicatorpos == 'right'){
12425 cls : 'control-label',
12429 html : this.fieldLabel
12443 labelCfg = cfg.cn[0];
12444 contentCfg = cfg.cn[1];
12447 if(this.labelWidth > 12){
12448 labelCfg.style = "width: " + this.labelWidth + 'px';
12451 if(this.labelWidth < 13 && this.labelmd == 0){
12452 this.labelmd = this.labelWidth;
12455 if(this.labellg > 0){
12456 labelCfg.cls += ' col-lg-' + this.labellg;
12457 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12460 if(this.labelmd > 0){
12461 labelCfg.cls += ' col-md-' + this.labelmd;
12462 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12465 if(this.labelsm > 0){
12466 labelCfg.cls += ' col-sm-' + this.labelsm;
12467 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12470 if(this.labelxs > 0){
12471 labelCfg.cls += ' col-xs-' + this.labelxs;
12472 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12475 } else if ( this.fieldLabel.length) {
12476 // Roo.log(" label");
12481 //cls : 'input-group-addon',
12482 html : this.fieldLabel
12490 if(this.indicatorpos == 'right'){
12498 html : this.fieldLabel
12512 // Roo.log(" no label && no align");
12519 ['xs','sm','md','lg'].map(function(size){
12520 if (settings[size]) {
12521 cfg.cls += ' col-' + size + '-' + settings[size];
12532 onResize : function(w, h){
12533 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12534 // if(typeof w == 'number'){
12535 // var x = w - this.trigger.getWidth();
12536 // this.inputEl().setWidth(this.adjustWidth('input', x));
12537 // this.trigger.setStyle('left', x+'px');
12542 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12545 getResizeEl : function(){
12546 return this.inputEl();
12550 getPositionEl : function(){
12551 return this.inputEl();
12555 alignErrorIcon : function(){
12556 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12560 initEvents : function(){
12564 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12565 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12566 if(!this.multiple && this.showToggleBtn){
12567 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12568 if(this.hideTrigger){
12569 this.trigger.setDisplayed(false);
12571 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12575 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12578 if(this.removable && !this.editable && !this.tickable){
12579 var close = this.closeTriggerEl();
12582 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12583 close.on('click', this.removeBtnClick, this, close);
12587 //this.trigger.addClassOnOver('x-form-trigger-over');
12588 //this.trigger.addClassOnClick('x-form-trigger-click');
12591 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12595 closeTriggerEl : function()
12597 var close = this.el.select('.roo-combo-removable-btn', true).first();
12598 return close ? close : false;
12601 removeBtnClick : function(e, h, el)
12603 e.preventDefault();
12605 if(this.fireEvent("remove", this) !== false){
12607 this.fireEvent("afterremove", this)
12611 createList : function()
12613 this.list = Roo.get(document.body).createChild({
12614 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12615 cls: 'typeahead typeahead-long dropdown-menu shadow',
12616 style: 'display:none'
12619 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12624 initTrigger : function(){
12629 onDestroy : function(){
12631 this.trigger.removeAllListeners();
12632 // this.trigger.remove();
12635 // this.wrap.remove();
12637 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12641 onFocus : function(){
12642 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12644 if(!this.mimicing){
12645 this.wrap.addClass('x-trigger-wrap-focus');
12646 this.mimicing = true;
12647 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12648 if(this.monitorTab){
12649 this.el.on("keydown", this.checkTab, this);
12656 checkTab : function(e){
12657 if(e.getKey() == e.TAB){
12658 this.triggerBlur();
12663 onBlur : function(){
12668 mimicBlur : function(e, t){
12670 if(!this.wrap.contains(t) && this.validateBlur()){
12671 this.triggerBlur();
12677 triggerBlur : function(){
12678 this.mimicing = false;
12679 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12680 if(this.monitorTab){
12681 this.el.un("keydown", this.checkTab, this);
12683 //this.wrap.removeClass('x-trigger-wrap-focus');
12684 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12688 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12689 validateBlur : function(e, t){
12694 onDisable : function(){
12695 this.inputEl().dom.disabled = true;
12696 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12698 // this.wrap.addClass('x-item-disabled');
12703 onEnable : function(){
12704 this.inputEl().dom.disabled = false;
12705 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12707 // this.el.removeClass('x-item-disabled');
12712 onShow : function(){
12713 var ae = this.getActionEl();
12716 ae.dom.style.display = '';
12717 ae.dom.style.visibility = 'visible';
12723 onHide : function(){
12724 var ae = this.getActionEl();
12725 ae.dom.style.display = 'none';
12729 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12730 * by an implementing function.
12732 * @param {EventObject} e
12734 onTriggerClick : Roo.emptyFn
12742 * @class Roo.bootstrap.CardUploader
12743 * @extends Roo.bootstrap.Button
12744 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12745 * @cfg {Number} errorTimeout default 3000
12746 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12747 * @cfg {Array} html The button text.
12751 * Create a new CardUploader
12752 * @param {Object} config The config object
12755 Roo.bootstrap.CardUploader = function(config){
12759 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12762 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12770 * When a image is clicked on - and needs to display a slideshow or similar..
12771 * @param {Roo.bootstrap.Card} this
12772 * @param {Object} The image information data
12778 * When a the download link is clicked
12779 * @param {Roo.bootstrap.Card} this
12780 * @param {Object} The image information data contains
12787 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12790 errorTimeout : 3000,
12794 fileCollection : false,
12797 getAutoCreate : function()
12801 cls :'form-group' ,
12806 //cls : 'input-group-addon',
12807 html : this.fieldLabel
12815 value : this.value,
12816 cls : 'd-none form-control'
12821 multiple : 'multiple',
12823 cls : 'd-none roo-card-upload-selector'
12827 cls : 'roo-card-uploader-button-container w-100 mb-2'
12830 cls : 'card-columns roo-card-uploader-container'
12840 getChildContainer : function() /// what children are added to.
12842 return this.containerEl;
12845 getButtonContainer : function() /// what children are added to.
12847 return this.el.select(".roo-card-uploader-button-container").first();
12850 initEvents : function()
12853 Roo.bootstrap.Input.prototype.initEvents.call(this);
12857 xns: Roo.bootstrap,
12860 container_method : 'getButtonContainer' ,
12861 html : this.html, // fix changable?
12864 'click' : function(btn, e) {
12873 this.urlAPI = (window.createObjectURL && window) ||
12874 (window.URL && URL.revokeObjectURL && URL) ||
12875 (window.webkitURL && webkitURL);
12880 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12882 this.selectorEl.on('change', this.onFileSelected, this);
12885 this.images.forEach(function(img) {
12888 this.images = false;
12890 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12896 onClick : function(e)
12898 e.preventDefault();
12900 this.selectorEl.dom.click();
12904 onFileSelected : function(e)
12906 e.preventDefault();
12908 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12912 Roo.each(this.selectorEl.dom.files, function(file){
12913 this.addFile(file);
12922 addFile : function(file)
12925 if(typeof(file) === 'string'){
12926 throw "Add file by name?"; // should not happen
12930 if(!file || !this.urlAPI){
12940 var url = _this.urlAPI.createObjectURL( file);
12943 id : Roo.bootstrap.CardUploader.ID--,
12944 is_uploaded : false,
12948 mimetype : file.type,
12956 * addCard - add an Attachment to the uploader
12957 * @param data - the data about the image to upload
12961 title : "Title of file",
12962 is_uploaded : false,
12963 src : "http://.....",
12964 srcfile : { the File upload object },
12965 mimetype : file.type,
12968 .. any other data...
12974 addCard : function (data)
12976 // hidden input element?
12977 // if the file is not an image...
12978 //then we need to use something other that and header_image
12983 xns : Roo.bootstrap,
12984 xtype : 'CardFooter',
12987 xns : Roo.bootstrap,
12993 xns : Roo.bootstrap,
12995 html : String.format("<small>{0}</small>", data.title),
12996 cls : 'col-10 text-left',
13001 click : function() {
13003 t.fireEvent( "download", t, data );
13009 xns : Roo.bootstrap,
13011 style: 'max-height: 28px; ',
13017 click : function() {
13018 t.removeCard(data.id)
13030 var cn = this.addxtype(
13033 xns : Roo.bootstrap,
13036 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13037 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13038 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13043 initEvents : function() {
13044 Roo.bootstrap.Card.prototype.initEvents.call(this);
13046 this.imgEl = this.el.select('.card-img-top').first();
13048 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13049 this.imgEl.set({ 'pointer' : 'cursor' });
13052 this.getCardFooter().addClass('p-1');
13059 // dont' really need ot update items.
13060 // this.items.push(cn);
13061 this.fileCollection.add(cn);
13063 if (!data.srcfile) {
13064 this.updateInput();
13069 var reader = new FileReader();
13070 reader.addEventListener("load", function() {
13071 data.srcdata = reader.result;
13074 reader.readAsDataURL(data.srcfile);
13079 removeCard : function(id)
13082 var card = this.fileCollection.get(id);
13083 card.data.is_deleted = 1;
13084 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13085 //this.fileCollection.remove(card);
13086 //this.items = this.items.filter(function(e) { return e != card });
13087 // dont' really need ot update items.
13088 card.el.dom.parentNode.removeChild(card.el.dom);
13089 this.updateInput();
13095 this.fileCollection.each(function(card) {
13096 if (card.el.dom && card.el.dom.parentNode) {
13097 card.el.dom.parentNode.removeChild(card.el.dom);
13100 this.fileCollection.clear();
13101 this.updateInput();
13104 updateInput : function()
13107 this.fileCollection.each(function(e) {
13111 this.inputEl().dom.value = JSON.stringify(data);
13121 Roo.bootstrap.CardUploader.ID = -1;/*
13123 * Ext JS Library 1.1.1
13124 * Copyright(c) 2006-2007, Ext JS, LLC.
13126 * Originally Released Under LGPL - original licence link has changed is not relivant.
13129 * <script type="text/javascript">
13134 * @class Roo.data.SortTypes
13136 * Defines the default sorting (casting?) comparison functions used when sorting data.
13138 Roo.data.SortTypes = {
13140 * Default sort that does nothing
13141 * @param {Mixed} s The value being converted
13142 * @return {Mixed} The comparison value
13144 none : function(s){
13149 * The regular expression used to strip tags
13153 stripTagsRE : /<\/?[^>]+>/gi,
13156 * Strips all HTML tags to sort on text only
13157 * @param {Mixed} s The value being converted
13158 * @return {String} The comparison value
13160 asText : function(s){
13161 return String(s).replace(this.stripTagsRE, "");
13165 * Strips all HTML tags to sort on text only - Case insensitive
13166 * @param {Mixed} s The value being converted
13167 * @return {String} The comparison value
13169 asUCText : function(s){
13170 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13174 * Case insensitive string
13175 * @param {Mixed} s The value being converted
13176 * @return {String} The comparison value
13178 asUCString : function(s) {
13179 return String(s).toUpperCase();
13184 * @param {Mixed} s The value being converted
13185 * @return {Number} The comparison value
13187 asDate : function(s) {
13191 if(s instanceof Date){
13192 return s.getTime();
13194 return Date.parse(String(s));
13199 * @param {Mixed} s The value being converted
13200 * @return {Float} The comparison value
13202 asFloat : function(s) {
13203 var val = parseFloat(String(s).replace(/,/g, ""));
13212 * @param {Mixed} s The value being converted
13213 * @return {Number} The comparison value
13215 asInt : function(s) {
13216 var val = parseInt(String(s).replace(/,/g, ""));
13224 * Ext JS Library 1.1.1
13225 * Copyright(c) 2006-2007, Ext JS, LLC.
13227 * Originally Released Under LGPL - original licence link has changed is not relivant.
13230 * <script type="text/javascript">
13234 * @class Roo.data.Record
13235 * Instances of this class encapsulate both record <em>definition</em> information, and record
13236 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13237 * to access Records cached in an {@link Roo.data.Store} object.<br>
13239 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13240 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13243 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13245 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13246 * {@link #create}. The parameters are the same.
13247 * @param {Array} data An associative Array of data values keyed by the field name.
13248 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13249 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13250 * not specified an integer id is generated.
13252 Roo.data.Record = function(data, id){
13253 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13258 * Generate a constructor for a specific record layout.
13259 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13260 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13261 * Each field definition object may contain the following properties: <ul>
13262 * <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,
13263 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13264 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13265 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13266 * is being used, then this is a string containing the javascript expression to reference the data relative to
13267 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13268 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13269 * this may be omitted.</p></li>
13270 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13271 * <ul><li>auto (Default, implies no conversion)</li>
13276 * <li>date</li></ul></p></li>
13277 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13278 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13279 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13280 * by the Reader into an object that will be stored in the Record. It is passed the
13281 * following parameters:<ul>
13282 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13284 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13286 * <br>usage:<br><pre><code>
13287 var TopicRecord = Roo.data.Record.create(
13288 {name: 'title', mapping: 'topic_title'},
13289 {name: 'author', mapping: 'username'},
13290 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13291 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13292 {name: 'lastPoster', mapping: 'user2'},
13293 {name: 'excerpt', mapping: 'post_text'}
13296 var myNewRecord = new TopicRecord({
13297 title: 'Do my job please',
13300 lastPost: new Date(),
13301 lastPoster: 'Animal',
13302 excerpt: 'No way dude!'
13304 myStore.add(myNewRecord);
13309 Roo.data.Record.create = function(o){
13310 var f = function(){
13311 f.superclass.constructor.apply(this, arguments);
13313 Roo.extend(f, Roo.data.Record);
13314 var p = f.prototype;
13315 p.fields = new Roo.util.MixedCollection(false, function(field){
13318 for(var i = 0, len = o.length; i < len; i++){
13319 p.fields.add(new Roo.data.Field(o[i]));
13321 f.getField = function(name){
13322 return p.fields.get(name);
13327 Roo.data.Record.AUTO_ID = 1000;
13328 Roo.data.Record.EDIT = 'edit';
13329 Roo.data.Record.REJECT = 'reject';
13330 Roo.data.Record.COMMIT = 'commit';
13332 Roo.data.Record.prototype = {
13334 * Readonly flag - true if this record has been modified.
13343 join : function(store){
13344 this.store = store;
13348 * Set the named field to the specified value.
13349 * @param {String} name The name of the field to set.
13350 * @param {Object} value The value to set the field to.
13352 set : function(name, value){
13353 if(this.data[name] == value){
13357 if(!this.modified){
13358 this.modified = {};
13360 if(typeof this.modified[name] == 'undefined'){
13361 this.modified[name] = this.data[name];
13363 this.data[name] = value;
13364 if(!this.editing && this.store){
13365 this.store.afterEdit(this);
13370 * Get the value of the named field.
13371 * @param {String} name The name of the field to get the value of.
13372 * @return {Object} The value of the field.
13374 get : function(name){
13375 return this.data[name];
13379 beginEdit : function(){
13380 this.editing = true;
13381 this.modified = {};
13385 cancelEdit : function(){
13386 this.editing = false;
13387 delete this.modified;
13391 endEdit : function(){
13392 this.editing = false;
13393 if(this.dirty && this.store){
13394 this.store.afterEdit(this);
13399 * Usually called by the {@link Roo.data.Store} which owns the Record.
13400 * Rejects all changes made to the Record since either creation, or the last commit operation.
13401 * Modified fields are reverted to their original values.
13403 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13404 * of reject operations.
13406 reject : function(){
13407 var m = this.modified;
13409 if(typeof m[n] != "function"){
13410 this.data[n] = m[n];
13413 this.dirty = false;
13414 delete this.modified;
13415 this.editing = false;
13417 this.store.afterReject(this);
13422 * Usually called by the {@link Roo.data.Store} which owns the Record.
13423 * Commits all changes made to the Record since either creation, or the last commit operation.
13425 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13426 * of commit operations.
13428 commit : function(){
13429 this.dirty = false;
13430 delete this.modified;
13431 this.editing = false;
13433 this.store.afterCommit(this);
13438 hasError : function(){
13439 return this.error != null;
13443 clearError : function(){
13448 * Creates a copy of this record.
13449 * @param {String} id (optional) A new record id if you don't want to use this record's id
13452 copy : function(newId) {
13453 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13457 * Ext JS Library 1.1.1
13458 * Copyright(c) 2006-2007, Ext JS, LLC.
13460 * Originally Released Under LGPL - original licence link has changed is not relivant.
13463 * <script type="text/javascript">
13469 * @class Roo.data.Store
13470 * @extends Roo.util.Observable
13471 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13472 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13474 * 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
13475 * has no knowledge of the format of the data returned by the Proxy.<br>
13477 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13478 * instances from the data object. These records are cached and made available through accessor functions.
13480 * Creates a new Store.
13481 * @param {Object} config A config object containing the objects needed for the Store to access data,
13482 * and read the data into Records.
13484 Roo.data.Store = function(config){
13485 this.data = new Roo.util.MixedCollection(false);
13486 this.data.getKey = function(o){
13489 this.baseParams = {};
13491 this.paramNames = {
13496 "multisort" : "_multisort"
13499 if(config && config.data){
13500 this.inlineData = config.data;
13501 delete config.data;
13504 Roo.apply(this, config);
13506 if(this.reader){ // reader passed
13507 this.reader = Roo.factory(this.reader, Roo.data);
13508 this.reader.xmodule = this.xmodule || false;
13509 if(!this.recordType){
13510 this.recordType = this.reader.recordType;
13512 if(this.reader.onMetaChange){
13513 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13517 if(this.recordType){
13518 this.fields = this.recordType.prototype.fields;
13520 this.modified = [];
13524 * @event datachanged
13525 * Fires when the data cache has changed, and a widget which is using this Store
13526 * as a Record cache should refresh its view.
13527 * @param {Store} this
13529 datachanged : true,
13531 * @event metachange
13532 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13533 * @param {Store} this
13534 * @param {Object} meta The JSON metadata
13539 * Fires when Records have been added to the Store
13540 * @param {Store} this
13541 * @param {Roo.data.Record[]} records The array of Records added
13542 * @param {Number} index The index at which the record(s) were added
13547 * Fires when a Record has been removed from the Store
13548 * @param {Store} this
13549 * @param {Roo.data.Record} record The Record that was removed
13550 * @param {Number} index The index at which the record was removed
13555 * Fires when a Record has been updated
13556 * @param {Store} this
13557 * @param {Roo.data.Record} record The Record that was updated
13558 * @param {String} operation The update operation being performed. Value may be one of:
13560 Roo.data.Record.EDIT
13561 Roo.data.Record.REJECT
13562 Roo.data.Record.COMMIT
13568 * Fires when the data cache has been cleared.
13569 * @param {Store} this
13573 * @event beforeload
13574 * Fires before a request is made for a new data object. If the beforeload handler returns false
13575 * the load action will be canceled.
13576 * @param {Store} this
13577 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13581 * @event beforeloadadd
13582 * Fires after a new set of Records has been loaded.
13583 * @param {Store} this
13584 * @param {Roo.data.Record[]} records The Records that were loaded
13585 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13587 beforeloadadd : true,
13590 * Fires after a new set of Records has been loaded, before they are added to the store.
13591 * @param {Store} this
13592 * @param {Roo.data.Record[]} records The Records that were loaded
13593 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13594 * @params {Object} return from reader
13598 * @event loadexception
13599 * Fires if an exception occurs in the Proxy during loading.
13600 * Called with the signature of the Proxy's "loadexception" event.
13601 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13604 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13605 * @param {Object} load options
13606 * @param {Object} jsonData from your request (normally this contains the Exception)
13608 loadexception : true
13612 this.proxy = Roo.factory(this.proxy, Roo.data);
13613 this.proxy.xmodule = this.xmodule || false;
13614 this.relayEvents(this.proxy, ["loadexception"]);
13616 this.sortToggle = {};
13617 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13619 Roo.data.Store.superclass.constructor.call(this);
13621 if(this.inlineData){
13622 this.loadData(this.inlineData);
13623 delete this.inlineData;
13627 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13629 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13630 * without a remote query - used by combo/forms at present.
13634 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13637 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13640 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13641 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13644 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13645 * on any HTTP request
13648 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13651 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13655 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13656 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13658 remoteSort : false,
13661 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13662 * loaded or when a record is removed. (defaults to false).
13664 pruneModifiedRecords : false,
13667 lastOptions : null,
13670 * Add Records to the Store and fires the add event.
13671 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13673 add : function(records){
13674 records = [].concat(records);
13675 for(var i = 0, len = records.length; i < len; i++){
13676 records[i].join(this);
13678 var index = this.data.length;
13679 this.data.addAll(records);
13680 this.fireEvent("add", this, records, index);
13684 * Remove a Record from the Store and fires the remove event.
13685 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13687 remove : function(record){
13688 var index = this.data.indexOf(record);
13689 this.data.removeAt(index);
13691 if(this.pruneModifiedRecords){
13692 this.modified.remove(record);
13694 this.fireEvent("remove", this, record, index);
13698 * Remove all Records from the Store and fires the clear event.
13700 removeAll : function(){
13702 if(this.pruneModifiedRecords){
13703 this.modified = [];
13705 this.fireEvent("clear", this);
13709 * Inserts Records to the Store at the given index and fires the add event.
13710 * @param {Number} index The start index at which to insert the passed Records.
13711 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13713 insert : function(index, records){
13714 records = [].concat(records);
13715 for(var i = 0, len = records.length; i < len; i++){
13716 this.data.insert(index, records[i]);
13717 records[i].join(this);
13719 this.fireEvent("add", this, records, index);
13723 * Get the index within the cache of the passed Record.
13724 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13725 * @return {Number} The index of the passed Record. Returns -1 if not found.
13727 indexOf : function(record){
13728 return this.data.indexOf(record);
13732 * Get the index within the cache of the Record with the passed id.
13733 * @param {String} id The id of the Record to find.
13734 * @return {Number} The index of the Record. Returns -1 if not found.
13736 indexOfId : function(id){
13737 return this.data.indexOfKey(id);
13741 * Get the Record with the specified id.
13742 * @param {String} id The id of the Record to find.
13743 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13745 getById : function(id){
13746 return this.data.key(id);
13750 * Get the Record at the specified index.
13751 * @param {Number} index The index of the Record to find.
13752 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13754 getAt : function(index){
13755 return this.data.itemAt(index);
13759 * Returns a range of Records between specified indices.
13760 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13761 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13762 * @return {Roo.data.Record[]} An array of Records
13764 getRange : function(start, end){
13765 return this.data.getRange(start, end);
13769 storeOptions : function(o){
13770 o = Roo.apply({}, o);
13773 this.lastOptions = o;
13777 * Loads the Record cache from the configured Proxy using the configured Reader.
13779 * If using remote paging, then the first load call must specify the <em>start</em>
13780 * and <em>limit</em> properties in the options.params property to establish the initial
13781 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13783 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13784 * and this call will return before the new data has been loaded. Perform any post-processing
13785 * in a callback function, or in a "load" event handler.</strong>
13787 * @param {Object} options An object containing properties which control loading options:<ul>
13788 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13789 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13790 * passed the following arguments:<ul>
13791 * <li>r : Roo.data.Record[]</li>
13792 * <li>options: Options object from the load call</li>
13793 * <li>success: Boolean success indicator</li></ul></li>
13794 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13795 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13798 load : function(options){
13799 options = options || {};
13800 if(this.fireEvent("beforeload", this, options) !== false){
13801 this.storeOptions(options);
13802 var p = Roo.apply(options.params || {}, this.baseParams);
13803 // if meta was not loaded from remote source.. try requesting it.
13804 if (!this.reader.metaFromRemote) {
13805 p._requestMeta = 1;
13807 if(this.sortInfo && this.remoteSort){
13808 var pn = this.paramNames;
13809 p[pn["sort"]] = this.sortInfo.field;
13810 p[pn["dir"]] = this.sortInfo.direction;
13812 if (this.multiSort) {
13813 var pn = this.paramNames;
13814 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13817 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13822 * Reloads the Record cache from the configured Proxy using the configured Reader and
13823 * the options from the last load operation performed.
13824 * @param {Object} options (optional) An object containing properties which may override the options
13825 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13826 * the most recently used options are reused).
13828 reload : function(options){
13829 this.load(Roo.applyIf(options||{}, this.lastOptions));
13833 // Called as a callback by the Reader during a load operation.
13834 loadRecords : function(o, options, success){
13835 if(!o || success === false){
13836 if(success !== false){
13837 this.fireEvent("load", this, [], options, o);
13839 if(options.callback){
13840 options.callback.call(options.scope || this, [], options, false);
13844 // if data returned failure - throw an exception.
13845 if (o.success === false) {
13846 // show a message if no listener is registered.
13847 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13848 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13850 // loadmask wil be hooked into this..
13851 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13854 var r = o.records, t = o.totalRecords || r.length;
13856 this.fireEvent("beforeloadadd", this, r, options, o);
13858 if(!options || options.add !== true){
13859 if(this.pruneModifiedRecords){
13860 this.modified = [];
13862 for(var i = 0, len = r.length; i < len; i++){
13866 this.data = this.snapshot;
13867 delete this.snapshot;
13870 this.data.addAll(r);
13871 this.totalLength = t;
13873 this.fireEvent("datachanged", this);
13875 this.totalLength = Math.max(t, this.data.length+r.length);
13879 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13881 var e = new Roo.data.Record({});
13883 e.set(this.parent.displayField, this.parent.emptyTitle);
13884 e.set(this.parent.valueField, '');
13889 this.fireEvent("load", this, r, options, o);
13890 if(options.callback){
13891 options.callback.call(options.scope || this, r, options, true);
13897 * Loads data from a passed data block. A Reader which understands the format of the data
13898 * must have been configured in the constructor.
13899 * @param {Object} data The data block from which to read the Records. The format of the data expected
13900 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13901 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13903 loadData : function(o, append){
13904 var r = this.reader.readRecords(o);
13905 this.loadRecords(r, {add: append}, true);
13909 * using 'cn' the nested child reader read the child array into it's child stores.
13910 * @param {Object} rec The record with a 'children array
13912 loadDataFromChildren : function(rec)
13914 this.loadData(this.reader.toLoadData(rec));
13919 * Gets the number of cached records.
13921 * <em>If using paging, this may not be the total size of the dataset. If the data object
13922 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13923 * the data set size</em>
13925 getCount : function(){
13926 return this.data.length || 0;
13930 * Gets the total number of records in the dataset as returned by the server.
13932 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13933 * the dataset size</em>
13935 getTotalCount : function(){
13936 return this.totalLength || 0;
13940 * Returns the sort state of the Store as an object with two properties:
13942 field {String} The name of the field by which the Records are sorted
13943 direction {String} The sort order, "ASC" or "DESC"
13946 getSortState : function(){
13947 return this.sortInfo;
13951 applySort : function(){
13952 if(this.sortInfo && !this.remoteSort){
13953 var s = this.sortInfo, f = s.field;
13954 var st = this.fields.get(f).sortType;
13955 var fn = function(r1, r2){
13956 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13957 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13959 this.data.sort(s.direction, fn);
13960 if(this.snapshot && this.snapshot != this.data){
13961 this.snapshot.sort(s.direction, fn);
13967 * Sets the default sort column and order to be used by the next load operation.
13968 * @param {String} fieldName The name of the field to sort by.
13969 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13971 setDefaultSort : function(field, dir){
13972 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13976 * Sort the Records.
13977 * If remote sorting is used, the sort is performed on the server, and the cache is
13978 * reloaded. If local sorting is used, the cache is sorted internally.
13979 * @param {String} fieldName The name of the field to sort by.
13980 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13982 sort : function(fieldName, dir){
13983 var f = this.fields.get(fieldName);
13985 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13987 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13988 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13993 this.sortToggle[f.name] = dir;
13994 this.sortInfo = {field: f.name, direction: dir};
13995 if(!this.remoteSort){
13997 this.fireEvent("datachanged", this);
13999 this.load(this.lastOptions);
14004 * Calls the specified function for each of the Records in the cache.
14005 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14006 * Returning <em>false</em> aborts and exits the iteration.
14007 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14009 each : function(fn, scope){
14010 this.data.each(fn, scope);
14014 * Gets all records modified since the last commit. Modified records are persisted across load operations
14015 * (e.g., during paging).
14016 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14018 getModifiedRecords : function(){
14019 return this.modified;
14023 createFilterFn : function(property, value, anyMatch){
14024 if(!value.exec){ // not a regex
14025 value = String(value);
14026 if(value.length == 0){
14029 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14031 return function(r){
14032 return value.test(r.data[property]);
14037 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14038 * @param {String} property A field on your records
14039 * @param {Number} start The record index to start at (defaults to 0)
14040 * @param {Number} end The last record index to include (defaults to length - 1)
14041 * @return {Number} The sum
14043 sum : function(property, start, end){
14044 var rs = this.data.items, v = 0;
14045 start = start || 0;
14046 end = (end || end === 0) ? end : rs.length-1;
14048 for(var i = start; i <= end; i++){
14049 v += (rs[i].data[property] || 0);
14055 * Filter the records by a specified property.
14056 * @param {String} field A field on your records
14057 * @param {String/RegExp} value Either a string that the field
14058 * should start with or a RegExp to test against the field
14059 * @param {Boolean} anyMatch True to match any part not just the beginning
14061 filter : function(property, value, anyMatch){
14062 var fn = this.createFilterFn(property, value, anyMatch);
14063 return fn ? this.filterBy(fn) : this.clearFilter();
14067 * Filter by a function. The specified function will be called with each
14068 * record in this data source. If the function returns true the record is included,
14069 * otherwise it is filtered.
14070 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14071 * @param {Object} scope (optional) The scope of the function (defaults to this)
14073 filterBy : function(fn, scope){
14074 this.snapshot = this.snapshot || this.data;
14075 this.data = this.queryBy(fn, scope||this);
14076 this.fireEvent("datachanged", this);
14080 * Query the records by a specified property.
14081 * @param {String} field A field on your records
14082 * @param {String/RegExp} value Either a string that the field
14083 * should start with or a RegExp to test against the field
14084 * @param {Boolean} anyMatch True to match any part not just the beginning
14085 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14087 query : function(property, value, anyMatch){
14088 var fn = this.createFilterFn(property, value, anyMatch);
14089 return fn ? this.queryBy(fn) : this.data.clone();
14093 * Query by a function. The specified function will be called with each
14094 * record in this data source. If the function returns true the record is included
14096 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14097 * @param {Object} scope (optional) The scope of the function (defaults to this)
14098 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14100 queryBy : function(fn, scope){
14101 var data = this.snapshot || this.data;
14102 return data.filterBy(fn, scope||this);
14106 * Collects unique values for a particular dataIndex from this store.
14107 * @param {String} dataIndex The property to collect
14108 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14109 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14110 * @return {Array} An array of the unique values
14112 collect : function(dataIndex, allowNull, bypassFilter){
14113 var d = (bypassFilter === true && this.snapshot) ?
14114 this.snapshot.items : this.data.items;
14115 var v, sv, r = [], l = {};
14116 for(var i = 0, len = d.length; i < len; i++){
14117 v = d[i].data[dataIndex];
14119 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14128 * Revert to a view of the Record cache with no filtering applied.
14129 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14131 clearFilter : function(suppressEvent){
14132 if(this.snapshot && this.snapshot != this.data){
14133 this.data = this.snapshot;
14134 delete this.snapshot;
14135 if(suppressEvent !== true){
14136 this.fireEvent("datachanged", this);
14142 afterEdit : function(record){
14143 if(this.modified.indexOf(record) == -1){
14144 this.modified.push(record);
14146 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14150 afterReject : function(record){
14151 this.modified.remove(record);
14152 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14156 afterCommit : function(record){
14157 this.modified.remove(record);
14158 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14162 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14163 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14165 commitChanges : function(){
14166 var m = this.modified.slice(0);
14167 this.modified = [];
14168 for(var i = 0, len = m.length; i < len; i++){
14174 * Cancel outstanding changes on all changed records.
14176 rejectChanges : function(){
14177 var m = this.modified.slice(0);
14178 this.modified = [];
14179 for(var i = 0, len = m.length; i < len; i++){
14184 onMetaChange : function(meta, rtype, o){
14185 this.recordType = rtype;
14186 this.fields = rtype.prototype.fields;
14187 delete this.snapshot;
14188 this.sortInfo = meta.sortInfo || this.sortInfo;
14189 this.modified = [];
14190 this.fireEvent('metachange', this, this.reader.meta);
14193 moveIndex : function(data, type)
14195 var index = this.indexOf(data);
14197 var newIndex = index + type;
14201 this.insert(newIndex, data);
14206 * Ext JS Library 1.1.1
14207 * Copyright(c) 2006-2007, Ext JS, LLC.
14209 * Originally Released Under LGPL - original licence link has changed is not relivant.
14212 * <script type="text/javascript">
14216 * @class Roo.data.SimpleStore
14217 * @extends Roo.data.Store
14218 * Small helper class to make creating Stores from Array data easier.
14219 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14220 * @cfg {Array} fields An array of field definition objects, or field name strings.
14221 * @cfg {Object} an existing reader (eg. copied from another store)
14222 * @cfg {Array} data The multi-dimensional array of data
14224 * @param {Object} config
14226 Roo.data.SimpleStore = function(config)
14228 Roo.data.SimpleStore.superclass.constructor.call(this, {
14230 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14233 Roo.data.Record.create(config.fields)
14235 proxy : new Roo.data.MemoryProxy(config.data)
14239 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14241 * Ext JS Library 1.1.1
14242 * Copyright(c) 2006-2007, Ext JS, LLC.
14244 * Originally Released Under LGPL - original licence link has changed is not relivant.
14247 * <script type="text/javascript">
14252 * @extends Roo.data.Store
14253 * @class Roo.data.JsonStore
14254 * Small helper class to make creating Stores for JSON data easier. <br/>
14256 var store = new Roo.data.JsonStore({
14257 url: 'get-images.php',
14259 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14262 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14263 * JsonReader and HttpProxy (unless inline data is provided).</b>
14264 * @cfg {Array} fields An array of field definition objects, or field name strings.
14266 * @param {Object} config
14268 Roo.data.JsonStore = function(c){
14269 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14270 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14271 reader: new Roo.data.JsonReader(c, c.fields)
14274 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14276 * Ext JS Library 1.1.1
14277 * Copyright(c) 2006-2007, Ext JS, LLC.
14279 * Originally Released Under LGPL - original licence link has changed is not relivant.
14282 * <script type="text/javascript">
14286 Roo.data.Field = function(config){
14287 if(typeof config == "string"){
14288 config = {name: config};
14290 Roo.apply(this, config);
14293 this.type = "auto";
14296 var st = Roo.data.SortTypes;
14297 // named sortTypes are supported, here we look them up
14298 if(typeof this.sortType == "string"){
14299 this.sortType = st[this.sortType];
14302 // set default sortType for strings and dates
14303 if(!this.sortType){
14306 this.sortType = st.asUCString;
14309 this.sortType = st.asDate;
14312 this.sortType = st.none;
14317 var stripRe = /[\$,%]/g;
14319 // prebuilt conversion function for this field, instead of
14320 // switching every time we're reading a value
14322 var cv, dateFormat = this.dateFormat;
14327 cv = function(v){ return v; };
14330 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14334 return v !== undefined && v !== null && v !== '' ?
14335 parseInt(String(v).replace(stripRe, ""), 10) : '';
14340 return v !== undefined && v !== null && v !== '' ?
14341 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14346 cv = function(v){ return v === true || v === "true" || v == 1; };
14353 if(v instanceof Date){
14357 if(dateFormat == "timestamp"){
14358 return new Date(v*1000);
14360 return Date.parseDate(v, dateFormat);
14362 var parsed = Date.parse(v);
14363 return parsed ? new Date(parsed) : null;
14372 Roo.data.Field.prototype = {
14380 * Ext JS Library 1.1.1
14381 * Copyright(c) 2006-2007, Ext JS, LLC.
14383 * Originally Released Under LGPL - original licence link has changed is not relivant.
14386 * <script type="text/javascript">
14389 // Base class for reading structured data from a data source. This class is intended to be
14390 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14393 * @class Roo.data.DataReader
14394 * Base class for reading structured data from a data source. This class is intended to be
14395 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14398 Roo.data.DataReader = function(meta, recordType){
14402 this.recordType = recordType instanceof Array ?
14403 Roo.data.Record.create(recordType) : recordType;
14406 Roo.data.DataReader.prototype = {
14409 readerType : 'Data',
14411 * Create an empty record
14412 * @param {Object} data (optional) - overlay some values
14413 * @return {Roo.data.Record} record created.
14415 newRow : function(d) {
14417 this.recordType.prototype.fields.each(function(c) {
14419 case 'int' : da[c.name] = 0; break;
14420 case 'date' : da[c.name] = new Date(); break;
14421 case 'float' : da[c.name] = 0.0; break;
14422 case 'boolean' : da[c.name] = false; break;
14423 default : da[c.name] = ""; break;
14427 return new this.recordType(Roo.apply(da, d));
14433 * Ext JS Library 1.1.1
14434 * Copyright(c) 2006-2007, Ext JS, LLC.
14436 * Originally Released Under LGPL - original licence link has changed is not relivant.
14439 * <script type="text/javascript">
14443 * @class Roo.data.DataProxy
14444 * @extends Roo.data.Observable
14445 * This class is an abstract base class for implementations which provide retrieval of
14446 * unformatted data objects.<br>
14448 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14449 * (of the appropriate type which knows how to parse the data object) to provide a block of
14450 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14452 * Custom implementations must implement the load method as described in
14453 * {@link Roo.data.HttpProxy#load}.
14455 Roo.data.DataProxy = function(){
14458 * @event beforeload
14459 * Fires before a network request is made to retrieve a data object.
14460 * @param {Object} This DataProxy object.
14461 * @param {Object} params The params parameter to the load function.
14466 * Fires before the load method's callback is called.
14467 * @param {Object} This DataProxy object.
14468 * @param {Object} o The data object.
14469 * @param {Object} arg The callback argument object passed to the load function.
14473 * @event loadexception
14474 * Fires if an Exception occurs during data retrieval.
14475 * @param {Object} This DataProxy object.
14476 * @param {Object} o The data object.
14477 * @param {Object} arg The callback argument object passed to the load function.
14478 * @param {Object} e The Exception.
14480 loadexception : true
14482 Roo.data.DataProxy.superclass.constructor.call(this);
14485 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14488 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14492 * Ext JS Library 1.1.1
14493 * Copyright(c) 2006-2007, Ext JS, LLC.
14495 * Originally Released Under LGPL - original licence link has changed is not relivant.
14498 * <script type="text/javascript">
14501 * @class Roo.data.MemoryProxy
14502 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14503 * to the Reader when its load method is called.
14505 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14507 Roo.data.MemoryProxy = function(data){
14511 Roo.data.MemoryProxy.superclass.constructor.call(this);
14515 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14518 * Load data from the requested source (in this case an in-memory
14519 * data object passed to the constructor), read the data object into
14520 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14521 * process that block using the passed callback.
14522 * @param {Object} params This parameter is not used by the MemoryProxy class.
14523 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14524 * object into a block of Roo.data.Records.
14525 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14526 * The function must be passed <ul>
14527 * <li>The Record block object</li>
14528 * <li>The "arg" argument from the load function</li>
14529 * <li>A boolean success indicator</li>
14531 * @param {Object} scope The scope in which to call the callback
14532 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14534 load : function(params, reader, callback, scope, arg){
14535 params = params || {};
14538 result = reader.readRecords(params.data ? params.data :this.data);
14540 this.fireEvent("loadexception", this, arg, null, e);
14541 callback.call(scope, null, arg, false);
14544 callback.call(scope, result, arg, true);
14548 update : function(params, records){
14553 * Ext JS Library 1.1.1
14554 * Copyright(c) 2006-2007, Ext JS, LLC.
14556 * Originally Released Under LGPL - original licence link has changed is not relivant.
14559 * <script type="text/javascript">
14562 * @class Roo.data.HttpProxy
14563 * @extends Roo.data.DataProxy
14564 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14565 * configured to reference a certain URL.<br><br>
14567 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14568 * from which the running page was served.<br><br>
14570 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14572 * Be aware that to enable the browser to parse an XML document, the server must set
14573 * the Content-Type header in the HTTP response to "text/xml".
14575 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14576 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14577 * will be used to make the request.
14579 Roo.data.HttpProxy = function(conn){
14580 Roo.data.HttpProxy.superclass.constructor.call(this);
14581 // is conn a conn config or a real conn?
14583 this.useAjax = !conn || !conn.events;
14587 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14588 // thse are take from connection...
14591 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14594 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14595 * extra parameters to each request made by this object. (defaults to undefined)
14598 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14599 * to each request made by this object. (defaults to undefined)
14602 * @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)
14605 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14608 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14614 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14618 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14619 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14620 * a finer-grained basis than the DataProxy events.
14622 getConnection : function(){
14623 return this.useAjax ? Roo.Ajax : this.conn;
14627 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14628 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14629 * process that block using the passed callback.
14630 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14631 * for the request to the remote server.
14632 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14633 * object into a block of Roo.data.Records.
14634 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14635 * The function must be passed <ul>
14636 * <li>The Record block object</li>
14637 * <li>The "arg" argument from the load function</li>
14638 * <li>A boolean success indicator</li>
14640 * @param {Object} scope The scope in which to call the callback
14641 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14643 load : function(params, reader, callback, scope, arg){
14644 if(this.fireEvent("beforeload", this, params) !== false){
14646 params : params || {},
14648 callback : callback,
14653 callback : this.loadResponse,
14657 Roo.applyIf(o, this.conn);
14658 if(this.activeRequest){
14659 Roo.Ajax.abort(this.activeRequest);
14661 this.activeRequest = Roo.Ajax.request(o);
14663 this.conn.request(o);
14666 callback.call(scope||this, null, arg, false);
14671 loadResponse : function(o, success, response){
14672 delete this.activeRequest;
14674 this.fireEvent("loadexception", this, o, response);
14675 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14680 result = o.reader.read(response);
14682 this.fireEvent("loadexception", this, o, response, e);
14683 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14687 this.fireEvent("load", this, o, o.request.arg);
14688 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14692 update : function(dataSet){
14697 updateResponse : function(dataSet){
14702 * Ext JS Library 1.1.1
14703 * Copyright(c) 2006-2007, Ext JS, LLC.
14705 * Originally Released Under LGPL - original licence link has changed is not relivant.
14708 * <script type="text/javascript">
14712 * @class Roo.data.ScriptTagProxy
14713 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14714 * other than the originating domain of the running page.<br><br>
14716 * <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
14717 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14719 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14720 * source code that is used as the source inside a <script> tag.<br><br>
14722 * In order for the browser to process the returned data, the server must wrap the data object
14723 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14724 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14725 * depending on whether the callback name was passed:
14728 boolean scriptTag = false;
14729 String cb = request.getParameter("callback");
14732 response.setContentType("text/javascript");
14734 response.setContentType("application/x-json");
14736 Writer out = response.getWriter();
14738 out.write(cb + "(");
14740 out.print(dataBlock.toJsonString());
14747 * @param {Object} config A configuration object.
14749 Roo.data.ScriptTagProxy = function(config){
14750 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14751 Roo.apply(this, config);
14752 this.head = document.getElementsByTagName("head")[0];
14755 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14757 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14759 * @cfg {String} url The URL from which to request the data object.
14762 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14766 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14767 * the server the name of the callback function set up by the load call to process the returned data object.
14768 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14769 * javascript output which calls this named function passing the data object as its only parameter.
14771 callbackParam : "callback",
14773 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14774 * name to the request.
14779 * Load data from the configured URL, read the data object into
14780 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14781 * process that block using the passed callback.
14782 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14783 * for the request to the remote server.
14784 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14785 * object into a block of Roo.data.Records.
14786 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14787 * The function must be passed <ul>
14788 * <li>The Record block object</li>
14789 * <li>The "arg" argument from the load function</li>
14790 * <li>A boolean success indicator</li>
14792 * @param {Object} scope The scope in which to call the callback
14793 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14795 load : function(params, reader, callback, scope, arg){
14796 if(this.fireEvent("beforeload", this, params) !== false){
14798 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14800 var url = this.url;
14801 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14803 url += "&_dc=" + (new Date().getTime());
14805 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14808 cb : "stcCallback"+transId,
14809 scriptId : "stcScript"+transId,
14813 callback : callback,
14819 window[trans.cb] = function(o){
14820 conn.handleResponse(o, trans);
14823 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14825 if(this.autoAbort !== false){
14829 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14831 var script = document.createElement("script");
14832 script.setAttribute("src", url);
14833 script.setAttribute("type", "text/javascript");
14834 script.setAttribute("id", trans.scriptId);
14835 this.head.appendChild(script);
14837 this.trans = trans;
14839 callback.call(scope||this, null, arg, false);
14844 isLoading : function(){
14845 return this.trans ? true : false;
14849 * Abort the current server request.
14851 abort : function(){
14852 if(this.isLoading()){
14853 this.destroyTrans(this.trans);
14858 destroyTrans : function(trans, isLoaded){
14859 this.head.removeChild(document.getElementById(trans.scriptId));
14860 clearTimeout(trans.timeoutId);
14862 window[trans.cb] = undefined;
14864 delete window[trans.cb];
14867 // if hasn't been loaded, wait for load to remove it to prevent script error
14868 window[trans.cb] = function(){
14869 window[trans.cb] = undefined;
14871 delete window[trans.cb];
14878 handleResponse : function(o, trans){
14879 this.trans = false;
14880 this.destroyTrans(trans, true);
14883 result = trans.reader.readRecords(o);
14885 this.fireEvent("loadexception", this, o, trans.arg, e);
14886 trans.callback.call(trans.scope||window, null, trans.arg, false);
14889 this.fireEvent("load", this, o, trans.arg);
14890 trans.callback.call(trans.scope||window, result, trans.arg, true);
14894 handleFailure : function(trans){
14895 this.trans = false;
14896 this.destroyTrans(trans, false);
14897 this.fireEvent("loadexception", this, null, trans.arg);
14898 trans.callback.call(trans.scope||window, null, trans.arg, false);
14902 * Ext JS Library 1.1.1
14903 * Copyright(c) 2006-2007, Ext JS, LLC.
14905 * Originally Released Under LGPL - original licence link has changed is not relivant.
14908 * <script type="text/javascript">
14912 * @class Roo.data.JsonReader
14913 * @extends Roo.data.DataReader
14914 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14915 * based on mappings in a provided Roo.data.Record constructor.
14917 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14918 * in the reply previously.
14923 var RecordDef = Roo.data.Record.create([
14924 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14925 {name: 'occupation'} // This field will use "occupation" as the mapping.
14927 var myReader = new Roo.data.JsonReader({
14928 totalProperty: "results", // The property which contains the total dataset size (optional)
14929 root: "rows", // The property which contains an Array of row objects
14930 id: "id" // The property within each row object that provides an ID for the record (optional)
14934 * This would consume a JSON file like this:
14936 { 'results': 2, 'rows': [
14937 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14938 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14941 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14942 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14943 * paged from the remote server.
14944 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14945 * @cfg {String} root name of the property which contains the Array of row objects.
14946 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14947 * @cfg {Array} fields Array of field definition objects
14949 * Create a new JsonReader
14950 * @param {Object} meta Metadata configuration options
14951 * @param {Object} recordType Either an Array of field definition objects,
14952 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14954 Roo.data.JsonReader = function(meta, recordType){
14957 // set some defaults:
14958 Roo.applyIf(meta, {
14959 totalProperty: 'total',
14960 successProperty : 'success',
14965 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14967 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14969 readerType : 'Json',
14972 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14973 * Used by Store query builder to append _requestMeta to params.
14976 metaFromRemote : false,
14978 * This method is only used by a DataProxy which has retrieved data from a remote server.
14979 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14980 * @return {Object} data A data block which is used by an Roo.data.Store object as
14981 * a cache of Roo.data.Records.
14983 read : function(response){
14984 var json = response.responseText;
14986 var o = /* eval:var:o */ eval("("+json+")");
14988 throw {message: "JsonReader.read: Json object not found"};
14994 this.metaFromRemote = true;
14995 this.meta = o.metaData;
14996 this.recordType = Roo.data.Record.create(o.metaData.fields);
14997 this.onMetaChange(this.meta, this.recordType, o);
14999 return this.readRecords(o);
15002 // private function a store will implement
15003 onMetaChange : function(meta, recordType, o){
15010 simpleAccess: function(obj, subsc) {
15017 getJsonAccessor: function(){
15019 return function(expr) {
15021 return(re.test(expr))
15022 ? new Function("obj", "return obj." + expr)
15027 return Roo.emptyFn;
15032 * Create a data block containing Roo.data.Records from an XML document.
15033 * @param {Object} o An object which contains an Array of row objects in the property specified
15034 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15035 * which contains the total size of the dataset.
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 readRecords : function(o){
15041 * After any data loads, the raw JSON data is available for further custom processing.
15045 var s = this.meta, Record = this.recordType,
15046 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15048 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15050 if(s.totalProperty) {
15051 this.getTotal = this.getJsonAccessor(s.totalProperty);
15053 if(s.successProperty) {
15054 this.getSuccess = this.getJsonAccessor(s.successProperty);
15056 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15058 var g = this.getJsonAccessor(s.id);
15059 this.getId = function(rec) {
15061 return (r === undefined || r === "") ? null : r;
15064 this.getId = function(){return null;};
15067 for(var jj = 0; jj < fl; jj++){
15069 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15070 this.ef[jj] = this.getJsonAccessor(map);
15074 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15075 if(s.totalProperty){
15076 var vt = parseInt(this.getTotal(o), 10);
15081 if(s.successProperty){
15082 var vs = this.getSuccess(o);
15083 if(vs === false || vs === 'false'){
15088 for(var i = 0; i < c; i++){
15091 var id = this.getId(n);
15092 for(var j = 0; j < fl; j++){
15094 var v = this.ef[j](n);
15096 Roo.log('missing convert for ' + f.name);
15100 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15102 var record = new Record(values, id);
15104 records[i] = record;
15110 totalRecords : totalRecords
15113 // used when loading children.. @see loadDataFromChildren
15114 toLoadData: function(rec)
15116 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15117 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15118 return { data : data, total : data.length };
15123 * Ext JS Library 1.1.1
15124 * Copyright(c) 2006-2007, Ext JS, LLC.
15126 * Originally Released Under LGPL - original licence link has changed is not relivant.
15129 * <script type="text/javascript">
15133 * @class Roo.data.ArrayReader
15134 * @extends Roo.data.DataReader
15135 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15136 * Each element of that Array represents a row of data fields. The
15137 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15138 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15142 var RecordDef = Roo.data.Record.create([
15143 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15144 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15146 var myReader = new Roo.data.ArrayReader({
15147 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15151 * This would consume an Array like this:
15153 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15157 * Create a new JsonReader
15158 * @param {Object} meta Metadata configuration options.
15159 * @param {Object|Array} recordType Either an Array of field definition objects
15161 * @cfg {Array} fields Array of field definition objects
15162 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15163 * as specified to {@link Roo.data.Record#create},
15164 * or an {@link Roo.data.Record} object
15167 * created using {@link Roo.data.Record#create}.
15169 Roo.data.ArrayReader = function(meta, recordType)
15171 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15174 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15177 * Create a data block containing Roo.data.Records from an XML document.
15178 * @param {Object} o An Array of row objects which represents the dataset.
15179 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15180 * a cache of Roo.data.Records.
15182 readRecords : function(o)
15184 var sid = this.meta ? this.meta.id : null;
15185 var recordType = this.recordType, fields = recordType.prototype.fields;
15188 for(var i = 0; i < root.length; i++){
15191 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15192 for(var j = 0, jlen = fields.length; j < jlen; j++){
15193 var f = fields.items[j];
15194 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15195 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15197 values[f.name] = v;
15199 var record = new recordType(values, id);
15201 records[records.length] = record;
15205 totalRecords : records.length
15208 // used when loading children.. @see loadDataFromChildren
15209 toLoadData: function(rec)
15211 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15212 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15223 * @class Roo.bootstrap.ComboBox
15224 * @extends Roo.bootstrap.TriggerField
15225 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15226 * @cfg {Boolean} append (true|false) default false
15227 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15228 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15229 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15230 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15231 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15232 * @cfg {Boolean} animate default true
15233 * @cfg {Boolean} emptyResultText only for touch device
15234 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15235 * @cfg {String} emptyTitle default ''
15236 * @cfg {Number} width fixed with? experimental
15238 * Create a new ComboBox.
15239 * @param {Object} config Configuration options
15241 Roo.bootstrap.ComboBox = function(config){
15242 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15246 * Fires when the dropdown list is expanded
15247 * @param {Roo.bootstrap.ComboBox} combo This combo box
15252 * Fires when the dropdown list is collapsed
15253 * @param {Roo.bootstrap.ComboBox} combo This combo box
15257 * @event beforeselect
15258 * Fires before a list item is selected. Return false to cancel the selection.
15259 * @param {Roo.bootstrap.ComboBox} combo This combo box
15260 * @param {Roo.data.Record} record The data record returned from the underlying store
15261 * @param {Number} index The index of the selected item in the dropdown list
15263 'beforeselect' : true,
15266 * Fires when a list item is selected
15267 * @param {Roo.bootstrap.ComboBox} combo This combo box
15268 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15269 * @param {Number} index The index of the selected item in the dropdown list
15273 * @event beforequery
15274 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15275 * The event object passed has these properties:
15276 * @param {Roo.bootstrap.ComboBox} combo This combo box
15277 * @param {String} query The query
15278 * @param {Boolean} forceAll true to force "all" query
15279 * @param {Boolean} cancel true to cancel the query
15280 * @param {Object} e The query event object
15282 'beforequery': true,
15285 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15286 * @param {Roo.bootstrap.ComboBox} combo This combo box
15291 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15292 * @param {Roo.bootstrap.ComboBox} combo This combo box
15293 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15298 * Fires when the remove value from the combobox array
15299 * @param {Roo.bootstrap.ComboBox} combo This combo box
15303 * @event afterremove
15304 * Fires when the remove value from the combobox array
15305 * @param {Roo.bootstrap.ComboBox} combo This combo box
15307 'afterremove' : true,
15309 * @event specialfilter
15310 * Fires when specialfilter
15311 * @param {Roo.bootstrap.ComboBox} combo This combo box
15313 'specialfilter' : true,
15316 * Fires when tick the element
15317 * @param {Roo.bootstrap.ComboBox} combo This combo box
15321 * @event touchviewdisplay
15322 * Fires when touch view require special display (default is using displayField)
15323 * @param {Roo.bootstrap.ComboBox} combo This combo box
15324 * @param {Object} cfg set html .
15326 'touchviewdisplay' : true
15331 this.tickItems = [];
15333 this.selectedIndex = -1;
15334 if(this.mode == 'local'){
15335 if(config.queryDelay === undefined){
15336 this.queryDelay = 10;
15338 if(config.minChars === undefined){
15344 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15347 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15348 * rendering into an Roo.Editor, defaults to false)
15351 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15352 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15355 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15358 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15359 * the dropdown list (defaults to undefined, with no header element)
15363 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15367 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15369 listWidth: undefined,
15371 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15372 * mode = 'remote' or 'text' if mode = 'local')
15374 displayField: undefined,
15377 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15378 * mode = 'remote' or 'value' if mode = 'local').
15379 * Note: use of a valueField requires the user make a selection
15380 * in order for a value to be mapped.
15382 valueField: undefined,
15384 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15389 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15390 * field's data value (defaults to the underlying DOM element's name)
15392 hiddenName: undefined,
15394 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15398 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15400 selectedClass: 'active',
15403 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15407 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15408 * anchor positions (defaults to 'tl-bl')
15410 listAlign: 'tl-bl?',
15412 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15416 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15417 * query specified by the allQuery config option (defaults to 'query')
15419 triggerAction: 'query',
15421 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15422 * (defaults to 4, does not apply if editable = false)
15426 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15427 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15431 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15432 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15436 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15437 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15441 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15442 * when editable = true (defaults to false)
15444 selectOnFocus:false,
15446 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15448 queryParam: 'query',
15450 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15451 * when mode = 'remote' (defaults to 'Loading...')
15453 loadingText: 'Loading...',
15455 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15459 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15463 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15464 * traditional select (defaults to true)
15468 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15472 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15476 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15477 * listWidth has a higher value)
15481 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15482 * allow the user to set arbitrary text into the field (defaults to false)
15484 forceSelection:false,
15486 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15487 * if typeAhead = true (defaults to 250)
15489 typeAheadDelay : 250,
15491 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15492 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15494 valueNotFoundText : undefined,
15496 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15498 blockFocus : false,
15501 * @cfg {Boolean} disableClear Disable showing of clear button.
15503 disableClear : false,
15505 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15507 alwaysQuery : false,
15510 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15515 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15517 invalidClass : "has-warning",
15520 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15522 validClass : "has-success",
15525 * @cfg {Boolean} specialFilter (true|false) special filter default false
15527 specialFilter : false,
15530 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15532 mobileTouchView : true,
15535 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15537 useNativeIOS : false,
15540 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15542 mobile_restrict_height : false,
15544 ios_options : false,
15556 btnPosition : 'right',
15557 triggerList : true,
15558 showToggleBtn : true,
15560 emptyResultText: 'Empty',
15561 triggerText : 'Select',
15565 // element that contains real text value.. (when hidden is used..)
15567 getAutoCreate : function()
15572 * Render classic select for iso
15575 if(Roo.isIOS && this.useNativeIOS){
15576 cfg = this.getAutoCreateNativeIOS();
15584 if(Roo.isTouch && this.mobileTouchView){
15585 cfg = this.getAutoCreateTouchView();
15592 if(!this.tickable){
15593 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15598 * ComboBox with tickable selections
15601 var align = this.labelAlign || this.parentLabelAlign();
15604 cls : 'form-group roo-combobox-tickable' //input-group
15607 var btn_text_select = '';
15608 var btn_text_done = '';
15609 var btn_text_cancel = '';
15611 if (this.btn_text_show) {
15612 btn_text_select = 'Select';
15613 btn_text_done = 'Done';
15614 btn_text_cancel = 'Cancel';
15619 cls : 'tickable-buttons',
15624 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15625 //html : this.triggerText
15626 html: btn_text_select
15632 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15634 html: btn_text_done
15640 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15642 html: btn_text_cancel
15648 buttons.cn.unshift({
15650 cls: 'roo-select2-search-field-input'
15656 Roo.each(buttons.cn, function(c){
15658 c.cls += ' btn-' + _this.size;
15661 if (_this.disabled) {
15668 style : 'display: contents',
15673 cls: 'form-hidden-field'
15677 cls: 'roo-select2-choices',
15681 cls: 'roo-select2-search-field',
15692 cls: 'roo-select2-container input-group roo-select2-container-multi',
15698 // cls: 'typeahead typeahead-long dropdown-menu',
15699 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15704 if(this.hasFeedback && !this.allowBlank){
15708 cls: 'glyphicon form-control-feedback'
15711 combobox.cn.push(feedback);
15718 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15719 tooltip : 'This field is required'
15721 if (Roo.bootstrap.version == 4) {
15724 style : 'display:none'
15727 if (align ==='left' && this.fieldLabel.length) {
15729 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15736 cls : 'control-label col-form-label',
15737 html : this.fieldLabel
15749 var labelCfg = cfg.cn[1];
15750 var contentCfg = cfg.cn[2];
15753 if(this.indicatorpos == 'right'){
15759 cls : 'control-label col-form-label',
15763 html : this.fieldLabel
15779 labelCfg = cfg.cn[0];
15780 contentCfg = cfg.cn[1];
15784 if(this.labelWidth > 12){
15785 labelCfg.style = "width: " + this.labelWidth + 'px';
15787 if(this.width * 1 > 0){
15788 contentCfg.style = "width: " + this.width + 'px';
15790 if(this.labelWidth < 13 && this.labelmd == 0){
15791 this.labelmd = this.labelWidth;
15794 if(this.labellg > 0){
15795 labelCfg.cls += ' col-lg-' + this.labellg;
15796 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15799 if(this.labelmd > 0){
15800 labelCfg.cls += ' col-md-' + this.labelmd;
15801 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15804 if(this.labelsm > 0){
15805 labelCfg.cls += ' col-sm-' + this.labelsm;
15806 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15809 if(this.labelxs > 0){
15810 labelCfg.cls += ' col-xs-' + this.labelxs;
15811 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15815 } else if ( this.fieldLabel.length) {
15816 // Roo.log(" label");
15821 //cls : 'input-group-addon',
15822 html : this.fieldLabel
15827 if(this.indicatorpos == 'right'){
15831 //cls : 'input-group-addon',
15832 html : this.fieldLabel
15842 // Roo.log(" no label && no align");
15849 ['xs','sm','md','lg'].map(function(size){
15850 if (settings[size]) {
15851 cfg.cls += ' col-' + size + '-' + settings[size];
15859 _initEventsCalled : false,
15862 initEvents: function()
15864 if (this._initEventsCalled) { // as we call render... prevent looping...
15867 this._initEventsCalled = true;
15870 throw "can not find store for combo";
15873 this.indicator = this.indicatorEl();
15875 this.store = Roo.factory(this.store, Roo.data);
15876 this.store.parent = this;
15878 // if we are building from html. then this element is so complex, that we can not really
15879 // use the rendered HTML.
15880 // so we have to trash and replace the previous code.
15881 if (Roo.XComponent.build_from_html) {
15882 // remove this element....
15883 var e = this.el.dom, k=0;
15884 while (e ) { e = e.previousSibling; ++k;}
15889 this.rendered = false;
15891 this.render(this.parent().getChildContainer(true), k);
15894 if(Roo.isIOS && this.useNativeIOS){
15895 this.initIOSView();
15903 if(Roo.isTouch && this.mobileTouchView){
15904 this.initTouchView();
15909 this.initTickableEvents();
15913 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15915 if(this.hiddenName){
15917 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15919 this.hiddenField.dom.value =
15920 this.hiddenValue !== undefined ? this.hiddenValue :
15921 this.value !== undefined ? this.value : '';
15923 // prevent input submission
15924 this.el.dom.removeAttribute('name');
15925 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15930 // this.el.dom.setAttribute('autocomplete', 'off');
15933 var cls = 'x-combo-list';
15935 //this.list = new Roo.Layer({
15936 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15942 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15943 _this.list.setWidth(lw);
15946 this.list.on('mouseover', this.onViewOver, this);
15947 this.list.on('mousemove', this.onViewMove, this);
15948 this.list.on('scroll', this.onViewScroll, this);
15951 this.list.swallowEvent('mousewheel');
15952 this.assetHeight = 0;
15955 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15956 this.assetHeight += this.header.getHeight();
15959 this.innerList = this.list.createChild({cls:cls+'-inner'});
15960 this.innerList.on('mouseover', this.onViewOver, this);
15961 this.innerList.on('mousemove', this.onViewMove, this);
15962 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15964 if(this.allowBlank && !this.pageSize && !this.disableClear){
15965 this.footer = this.list.createChild({cls:cls+'-ft'});
15966 this.pageTb = new Roo.Toolbar(this.footer);
15970 this.footer = this.list.createChild({cls:cls+'-ft'});
15971 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15972 {pageSize: this.pageSize});
15976 if (this.pageTb && this.allowBlank && !this.disableClear) {
15978 this.pageTb.add(new Roo.Toolbar.Fill(), {
15979 cls: 'x-btn-icon x-btn-clear',
15981 handler: function()
15984 _this.clearValue();
15985 _this.onSelect(false, -1);
15990 this.assetHeight += this.footer.getHeight();
15995 this.tpl = Roo.bootstrap.version == 4 ?
15996 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15997 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16000 this.view = new Roo.View(this.list, this.tpl, {
16001 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16003 //this.view.wrapEl.setDisplayed(false);
16004 this.view.on('click', this.onViewClick, this);
16007 this.store.on('beforeload', this.onBeforeLoad, this);
16008 this.store.on('load', this.onLoad, this);
16009 this.store.on('loadexception', this.onLoadException, this);
16011 if(this.resizable){
16012 this.resizer = new Roo.Resizable(this.list, {
16013 pinned:true, handles:'se'
16015 this.resizer.on('resize', function(r, w, h){
16016 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16017 this.listWidth = w;
16018 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16019 this.restrictHeight();
16021 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16024 if(!this.editable){
16025 this.editable = true;
16026 this.setEditable(false);
16031 if (typeof(this.events.add.listeners) != 'undefined') {
16033 this.addicon = this.wrap.createChild(
16034 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16036 this.addicon.on('click', function(e) {
16037 this.fireEvent('add', this);
16040 if (typeof(this.events.edit.listeners) != 'undefined') {
16042 this.editicon = this.wrap.createChild(
16043 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16044 if (this.addicon) {
16045 this.editicon.setStyle('margin-left', '40px');
16047 this.editicon.on('click', function(e) {
16049 // we fire even if inothing is selected..
16050 this.fireEvent('edit', this, this.lastData );
16056 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16057 "up" : function(e){
16058 this.inKeyMode = true;
16062 "down" : function(e){
16063 if(!this.isExpanded()){
16064 this.onTriggerClick();
16066 this.inKeyMode = true;
16071 "enter" : function(e){
16072 // this.onViewClick();
16076 if(this.fireEvent("specialkey", this, e)){
16077 this.onViewClick(false);
16083 "esc" : function(e){
16087 "tab" : function(e){
16090 if(this.fireEvent("specialkey", this, e)){
16091 this.onViewClick(false);
16099 doRelay : function(foo, bar, hname){
16100 if(hname == 'down' || this.scope.isExpanded()){
16101 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16110 this.queryDelay = Math.max(this.queryDelay || 10,
16111 this.mode == 'local' ? 10 : 250);
16114 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16116 if(this.typeAhead){
16117 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16119 if(this.editable !== false){
16120 this.inputEl().on("keyup", this.onKeyUp, this);
16122 if(this.forceSelection){
16123 this.inputEl().on('blur', this.doForce, this);
16127 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16128 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16132 initTickableEvents: function()
16136 if(this.hiddenName){
16138 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16140 this.hiddenField.dom.value =
16141 this.hiddenValue !== undefined ? this.hiddenValue :
16142 this.value !== undefined ? this.value : '';
16144 // prevent input submission
16145 this.el.dom.removeAttribute('name');
16146 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16151 // this.list = this.el.select('ul.dropdown-menu',true).first();
16153 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16154 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16155 if(this.triggerList){
16156 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16159 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16160 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16162 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16163 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16165 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16166 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16168 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16169 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16170 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16173 this.cancelBtn.hide();
16178 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16179 _this.list.setWidth(lw);
16182 this.list.on('mouseover', this.onViewOver, this);
16183 this.list.on('mousemove', this.onViewMove, this);
16185 this.list.on('scroll', this.onViewScroll, this);
16188 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16189 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16192 this.view = new Roo.View(this.list, this.tpl, {
16197 selectedClass: this.selectedClass
16200 //this.view.wrapEl.setDisplayed(false);
16201 this.view.on('click', this.onViewClick, this);
16205 this.store.on('beforeload', this.onBeforeLoad, this);
16206 this.store.on('load', this.onLoad, this);
16207 this.store.on('loadexception', this.onLoadException, this);
16210 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16211 "up" : function(e){
16212 this.inKeyMode = true;
16216 "down" : function(e){
16217 this.inKeyMode = true;
16221 "enter" : function(e){
16222 if(this.fireEvent("specialkey", this, e)){
16223 this.onViewClick(false);
16229 "esc" : function(e){
16230 this.onTickableFooterButtonClick(e, false, false);
16233 "tab" : function(e){
16234 this.fireEvent("specialkey", this, e);
16236 this.onTickableFooterButtonClick(e, false, false);
16243 doRelay : function(e, fn, key){
16244 if(this.scope.isExpanded()){
16245 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16254 this.queryDelay = Math.max(this.queryDelay || 10,
16255 this.mode == 'local' ? 10 : 250);
16258 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16260 if(this.typeAhead){
16261 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16264 if(this.editable !== false){
16265 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16268 this.indicator = this.indicatorEl();
16270 if(this.indicator){
16271 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16272 this.indicator.hide();
16277 onDestroy : function(){
16279 this.view.setStore(null);
16280 this.view.el.removeAllListeners();
16281 this.view.el.remove();
16282 this.view.purgeListeners();
16285 this.list.dom.innerHTML = '';
16289 this.store.un('beforeload', this.onBeforeLoad, this);
16290 this.store.un('load', this.onLoad, this);
16291 this.store.un('loadexception', this.onLoadException, this);
16293 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16297 fireKey : function(e){
16298 if(e.isNavKeyPress() && !this.list.isVisible()){
16299 this.fireEvent("specialkey", this, e);
16304 onResize: function(w, h)
16308 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16310 // if(typeof w != 'number'){
16311 // // we do not handle it!?!?
16314 // var tw = this.trigger.getWidth();
16315 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16316 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16318 // this.inputEl().setWidth( this.adjustWidth('input', x));
16320 // //this.trigger.setStyle('left', x+'px');
16322 // if(this.list && this.listWidth === undefined){
16323 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16324 // this.list.setWidth(lw);
16325 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16333 * Allow or prevent the user from directly editing the field text. If false is passed,
16334 * the user will only be able to select from the items defined in the dropdown list. This method
16335 * is the runtime equivalent of setting the 'editable' config option at config time.
16336 * @param {Boolean} value True to allow the user to directly edit the field text
16338 setEditable : function(value){
16339 if(value == this.editable){
16342 this.editable = value;
16344 this.inputEl().dom.setAttribute('readOnly', true);
16345 this.inputEl().on('mousedown', this.onTriggerClick, this);
16346 this.inputEl().addClass('x-combo-noedit');
16348 this.inputEl().dom.setAttribute('readOnly', false);
16349 this.inputEl().un('mousedown', this.onTriggerClick, this);
16350 this.inputEl().removeClass('x-combo-noedit');
16356 onBeforeLoad : function(combo,opts){
16357 if(!this.hasFocus){
16361 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16363 this.restrictHeight();
16364 this.selectedIndex = -1;
16368 onLoad : function(){
16370 this.hasQuery = false;
16372 if(!this.hasFocus){
16376 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16377 this.loading.hide();
16380 if(this.store.getCount() > 0){
16383 this.restrictHeight();
16384 if(this.lastQuery == this.allQuery){
16385 if(this.editable && !this.tickable){
16386 this.inputEl().dom.select();
16390 !this.selectByValue(this.value, true) &&
16393 !this.store.lastOptions ||
16394 typeof(this.store.lastOptions.add) == 'undefined' ||
16395 this.store.lastOptions.add != true
16398 this.select(0, true);
16401 if(this.autoFocus){
16404 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16405 this.taTask.delay(this.typeAheadDelay);
16409 this.onEmptyResults();
16415 onLoadException : function()
16417 this.hasQuery = false;
16419 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16420 this.loading.hide();
16423 if(this.tickable && this.editable){
16428 // only causes errors at present
16429 //Roo.log(this.store.reader.jsonData);
16430 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16432 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16438 onTypeAhead : function(){
16439 if(this.store.getCount() > 0){
16440 var r = this.store.getAt(0);
16441 var newValue = r.data[this.displayField];
16442 var len = newValue.length;
16443 var selStart = this.getRawValue().length;
16445 if(selStart != len){
16446 this.setRawValue(newValue);
16447 this.selectText(selStart, newValue.length);
16453 onSelect : function(record, index){
16455 if(this.fireEvent('beforeselect', this, record, index) !== false){
16457 this.setFromData(index > -1 ? record.data : false);
16460 this.fireEvent('select', this, record, index);
16465 * Returns the currently selected field value or empty string if no value is set.
16466 * @return {String} value The selected value
16468 getValue : function()
16470 if(Roo.isIOS && this.useNativeIOS){
16471 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16475 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16478 if(this.valueField){
16479 return typeof this.value != 'undefined' ? this.value : '';
16481 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16485 getRawValue : function()
16487 if(Roo.isIOS && this.useNativeIOS){
16488 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16491 var v = this.inputEl().getValue();
16497 * Clears any text/value currently set in the field
16499 clearValue : function(){
16501 if(this.hiddenField){
16502 this.hiddenField.dom.value = '';
16505 this.setRawValue('');
16506 this.lastSelectionText = '';
16507 this.lastData = false;
16509 var close = this.closeTriggerEl();
16520 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16521 * will be displayed in the field. If the value does not match the data value of an existing item,
16522 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16523 * Otherwise the field will be blank (although the value will still be set).
16524 * @param {String} value The value to match
16526 setValue : function(v)
16528 if(Roo.isIOS && this.useNativeIOS){
16529 this.setIOSValue(v);
16539 if(this.valueField){
16540 var r = this.findRecord(this.valueField, v);
16542 text = r.data[this.displayField];
16543 }else if(this.valueNotFoundText !== undefined){
16544 text = this.valueNotFoundText;
16547 this.lastSelectionText = text;
16548 if(this.hiddenField){
16549 this.hiddenField.dom.value = v;
16551 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16554 var close = this.closeTriggerEl();
16557 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16563 * @property {Object} the last set data for the element
16568 * Sets the value of the field based on a object which is related to the record format for the store.
16569 * @param {Object} value the value to set as. or false on reset?
16571 setFromData : function(o){
16578 var dv = ''; // display value
16579 var vv = ''; // value value..
16581 if (this.displayField) {
16582 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16584 // this is an error condition!!!
16585 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16588 if(this.valueField){
16589 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16592 var close = this.closeTriggerEl();
16595 if(dv.length || vv * 1 > 0){
16597 this.blockFocus=true;
16603 if(this.hiddenField){
16604 this.hiddenField.dom.value = vv;
16606 this.lastSelectionText = dv;
16607 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16611 // no hidden field.. - we store the value in 'value', but still display
16612 // display field!!!!
16613 this.lastSelectionText = dv;
16614 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16621 reset : function(){
16622 // overridden so that last data is reset..
16629 this.setValue(this.originalValue);
16630 //this.clearInvalid();
16631 this.lastData = false;
16633 this.view.clearSelections();
16639 findRecord : function(prop, value){
16641 if(this.store.getCount() > 0){
16642 this.store.each(function(r){
16643 if(r.data[prop] == value){
16653 getName: function()
16655 // returns hidden if it's set..
16656 if (!this.rendered) {return ''};
16657 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16661 onViewMove : function(e, t){
16662 this.inKeyMode = false;
16666 onViewOver : function(e, t){
16667 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16670 var item = this.view.findItemFromChild(t);
16673 var index = this.view.indexOf(item);
16674 this.select(index, false);
16679 onViewClick : function(view, doFocus, el, e)
16681 var index = this.view.getSelectedIndexes()[0];
16683 var r = this.store.getAt(index);
16687 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16694 Roo.each(this.tickItems, function(v,k){
16696 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16698 _this.tickItems.splice(k, 1);
16700 if(typeof(e) == 'undefined' && view == false){
16701 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16713 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16714 this.tickItems.push(r.data);
16717 if(typeof(e) == 'undefined' && view == false){
16718 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16725 this.onSelect(r, index);
16727 if(doFocus !== false && !this.blockFocus){
16728 this.inputEl().focus();
16733 restrictHeight : function(){
16734 //this.innerList.dom.style.height = '';
16735 //var inner = this.innerList.dom;
16736 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16737 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16738 //this.list.beginUpdate();
16739 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16740 this.list.alignTo(this.inputEl(), this.listAlign);
16741 this.list.alignTo(this.inputEl(), this.listAlign);
16742 //this.list.endUpdate();
16746 onEmptyResults : function(){
16748 if(this.tickable && this.editable){
16749 this.hasFocus = false;
16750 this.restrictHeight();
16758 * Returns true if the dropdown list is expanded, else false.
16760 isExpanded : function(){
16761 return this.list.isVisible();
16765 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16766 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16767 * @param {String} value The data value of the item to select
16768 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16769 * selected item if it is not currently in view (defaults to true)
16770 * @return {Boolean} True if the value matched an item in the list, else false
16772 selectByValue : function(v, scrollIntoView){
16773 if(v !== undefined && v !== null){
16774 var r = this.findRecord(this.valueField || this.displayField, v);
16776 this.select(this.store.indexOf(r), scrollIntoView);
16784 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16785 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16786 * @param {Number} index The zero-based index of the list item to select
16787 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16788 * selected item if it is not currently in view (defaults to true)
16790 select : function(index, scrollIntoView){
16791 this.selectedIndex = index;
16792 this.view.select(index);
16793 if(scrollIntoView !== false){
16794 var el = this.view.getNode(index);
16796 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16799 this.list.scrollChildIntoView(el, false);
16805 selectNext : function(){
16806 var ct = this.store.getCount();
16808 if(this.selectedIndex == -1){
16810 }else if(this.selectedIndex < ct-1){
16811 this.select(this.selectedIndex+1);
16817 selectPrev : function(){
16818 var ct = this.store.getCount();
16820 if(this.selectedIndex == -1){
16822 }else if(this.selectedIndex != 0){
16823 this.select(this.selectedIndex-1);
16829 onKeyUp : function(e){
16830 if(this.editable !== false && !e.isSpecialKey()){
16831 this.lastKey = e.getKey();
16832 this.dqTask.delay(this.queryDelay);
16837 validateBlur : function(){
16838 return !this.list || !this.list.isVisible();
16842 initQuery : function(){
16844 var v = this.getRawValue();
16846 if(this.tickable && this.editable){
16847 v = this.tickableInputEl().getValue();
16854 doForce : function(){
16855 if(this.inputEl().dom.value.length > 0){
16856 this.inputEl().dom.value =
16857 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16863 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16864 * query allowing the query action to be canceled if needed.
16865 * @param {String} query The SQL query to execute
16866 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16867 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16868 * saved in the current store (defaults to false)
16870 doQuery : function(q, forceAll){
16872 if(q === undefined || q === null){
16877 forceAll: forceAll,
16881 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16886 forceAll = qe.forceAll;
16887 if(forceAll === true || (q.length >= this.minChars)){
16889 this.hasQuery = true;
16891 if(this.lastQuery != q || this.alwaysQuery){
16892 this.lastQuery = q;
16893 if(this.mode == 'local'){
16894 this.selectedIndex = -1;
16896 this.store.clearFilter();
16899 if(this.specialFilter){
16900 this.fireEvent('specialfilter', this);
16905 this.store.filter(this.displayField, q);
16908 this.store.fireEvent("datachanged", this.store);
16915 this.store.baseParams[this.queryParam] = q;
16917 var options = {params : this.getParams(q)};
16920 options.add = true;
16921 options.params.start = this.page * this.pageSize;
16924 this.store.load(options);
16927 * this code will make the page width larger, at the beginning, the list not align correctly,
16928 * we should expand the list on onLoad
16929 * so command out it
16934 this.selectedIndex = -1;
16939 this.loadNext = false;
16943 getParams : function(q){
16945 //p[this.queryParam] = q;
16949 p.limit = this.pageSize;
16955 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16957 collapse : function(){
16958 if(!this.isExpanded()){
16964 this.hasFocus = false;
16968 this.cancelBtn.hide();
16969 this.trigger.show();
16972 this.tickableInputEl().dom.value = '';
16973 this.tickableInputEl().blur();
16978 Roo.get(document).un('mousedown', this.collapseIf, this);
16979 Roo.get(document).un('mousewheel', this.collapseIf, this);
16980 if (!this.editable) {
16981 Roo.get(document).un('keydown', this.listKeyPress, this);
16983 this.fireEvent('collapse', this);
16989 collapseIf : function(e){
16990 var in_combo = e.within(this.el);
16991 var in_list = e.within(this.list);
16992 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16994 if (in_combo || in_list || is_list) {
16995 //e.stopPropagation();
17000 this.onTickableFooterButtonClick(e, false, false);
17008 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17010 expand : function(){
17012 if(this.isExpanded() || !this.hasFocus){
17016 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17017 this.list.setWidth(lw);
17023 this.restrictHeight();
17027 this.tickItems = Roo.apply([], this.item);
17030 this.cancelBtn.show();
17031 this.trigger.hide();
17034 this.tickableInputEl().focus();
17039 Roo.get(document).on('mousedown', this.collapseIf, this);
17040 Roo.get(document).on('mousewheel', this.collapseIf, this);
17041 if (!this.editable) {
17042 Roo.get(document).on('keydown', this.listKeyPress, this);
17045 this.fireEvent('expand', this);
17049 // Implements the default empty TriggerField.onTriggerClick function
17050 onTriggerClick : function(e)
17052 Roo.log('trigger click');
17054 if(this.disabled || !this.triggerList){
17059 this.loadNext = false;
17061 if(this.isExpanded()){
17063 if (!this.blockFocus) {
17064 this.inputEl().focus();
17068 this.hasFocus = true;
17069 if(this.triggerAction == 'all') {
17070 this.doQuery(this.allQuery, true);
17072 this.doQuery(this.getRawValue());
17074 if (!this.blockFocus) {
17075 this.inputEl().focus();
17080 onTickableTriggerClick : function(e)
17087 this.loadNext = false;
17088 this.hasFocus = true;
17090 if(this.triggerAction == 'all') {
17091 this.doQuery(this.allQuery, true);
17093 this.doQuery(this.getRawValue());
17097 onSearchFieldClick : function(e)
17099 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17100 this.onTickableFooterButtonClick(e, false, false);
17104 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17109 this.loadNext = false;
17110 this.hasFocus = true;
17112 if(this.triggerAction == 'all') {
17113 this.doQuery(this.allQuery, true);
17115 this.doQuery(this.getRawValue());
17119 listKeyPress : function(e)
17121 //Roo.log('listkeypress');
17122 // scroll to first matching element based on key pres..
17123 if (e.isSpecialKey()) {
17126 var k = String.fromCharCode(e.getKey()).toUpperCase();
17129 var csel = this.view.getSelectedNodes();
17130 var cselitem = false;
17132 var ix = this.view.indexOf(csel[0]);
17133 cselitem = this.store.getAt(ix);
17134 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17140 this.store.each(function(v) {
17142 // start at existing selection.
17143 if (cselitem.id == v.id) {
17149 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17150 match = this.store.indexOf(v);
17156 if (match === false) {
17157 return true; // no more action?
17160 this.view.select(match);
17161 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17162 sn.scrollIntoView(sn.dom.parentNode, false);
17165 onViewScroll : function(e, t){
17167 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){
17171 this.hasQuery = true;
17173 this.loading = this.list.select('.loading', true).first();
17175 if(this.loading === null){
17176 this.list.createChild({
17178 cls: 'loading roo-select2-more-results roo-select2-active',
17179 html: 'Loading more results...'
17182 this.loading = this.list.select('.loading', true).first();
17184 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17186 this.loading.hide();
17189 this.loading.show();
17194 this.loadNext = true;
17196 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17201 addItem : function(o)
17203 var dv = ''; // display value
17205 if (this.displayField) {
17206 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17208 // this is an error condition!!!
17209 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17216 var choice = this.choices.createChild({
17218 cls: 'roo-select2-search-choice',
17227 cls: 'roo-select2-search-choice-close fa fa-times',
17232 }, this.searchField);
17234 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17236 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17244 this.inputEl().dom.value = '';
17249 onRemoveItem : function(e, _self, o)
17251 e.preventDefault();
17253 this.lastItem = Roo.apply([], this.item);
17255 var index = this.item.indexOf(o.data) * 1;
17258 Roo.log('not this item?!');
17262 this.item.splice(index, 1);
17267 this.fireEvent('remove', this, e);
17273 syncValue : function()
17275 if(!this.item.length){
17282 Roo.each(this.item, function(i){
17283 if(_this.valueField){
17284 value.push(i[_this.valueField]);
17291 this.value = value.join(',');
17293 if(this.hiddenField){
17294 this.hiddenField.dom.value = this.value;
17297 this.store.fireEvent("datachanged", this.store);
17302 clearItem : function()
17304 if(!this.multiple){
17310 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17318 if(this.tickable && !Roo.isTouch){
17319 this.view.refresh();
17323 inputEl: function ()
17325 if(Roo.isIOS && this.useNativeIOS){
17326 return this.el.select('select.roo-ios-select', true).first();
17329 if(Roo.isTouch && this.mobileTouchView){
17330 return this.el.select('input.form-control',true).first();
17334 return this.searchField;
17337 return this.el.select('input.form-control',true).first();
17340 onTickableFooterButtonClick : function(e, btn, el)
17342 e.preventDefault();
17344 this.lastItem = Roo.apply([], this.item);
17346 if(btn && btn.name == 'cancel'){
17347 this.tickItems = Roo.apply([], this.item);
17356 Roo.each(this.tickItems, function(o){
17364 validate : function()
17366 if(this.getVisibilityEl().hasClass('hidden')){
17370 var v = this.getRawValue();
17373 v = this.getValue();
17376 if(this.disabled || this.allowBlank || v.length){
17381 this.markInvalid();
17385 tickableInputEl : function()
17387 if(!this.tickable || !this.editable){
17388 return this.inputEl();
17391 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17395 getAutoCreateTouchView : function()
17400 cls: 'form-group' //input-group
17406 type : this.inputType,
17407 cls : 'form-control x-combo-noedit',
17408 autocomplete: 'new-password',
17409 placeholder : this.placeholder || '',
17414 input.name = this.name;
17418 input.cls += ' input-' + this.size;
17421 if (this.disabled) {
17422 input.disabled = true;
17426 cls : 'roo-combobox-wrap',
17433 inputblock.cls += ' input-group';
17435 inputblock.cn.unshift({
17437 cls : 'input-group-addon input-group-prepend input-group-text',
17442 if(this.removable && !this.multiple){
17443 inputblock.cls += ' roo-removable';
17445 inputblock.cn.push({
17448 cls : 'roo-combo-removable-btn close'
17452 if(this.hasFeedback && !this.allowBlank){
17454 inputblock.cls += ' has-feedback';
17456 inputblock.cn.push({
17458 cls: 'glyphicon form-control-feedback'
17465 inputblock.cls += (this.before) ? '' : ' input-group';
17467 inputblock.cn.push({
17469 cls : 'input-group-addon input-group-append input-group-text',
17475 var ibwrap = inputblock;
17480 cls: 'roo-select2-choices',
17484 cls: 'roo-select2-search-field',
17497 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17502 cls: 'form-hidden-field'
17508 if(!this.multiple && this.showToggleBtn){
17514 if (this.caret != false) {
17517 cls: 'fa fa-' + this.caret
17524 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17526 Roo.bootstrap.version == 3 ? caret : '',
17529 cls: 'combobox-clear',
17543 combobox.cls += ' roo-select2-container-multi';
17546 var align = this.labelAlign || this.parentLabelAlign();
17548 if (align ==='left' && this.fieldLabel.length) {
17553 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17554 tooltip : 'This field is required'
17558 cls : 'control-label col-form-label',
17559 html : this.fieldLabel
17563 cls : 'roo-combobox-wrap ',
17570 var labelCfg = cfg.cn[1];
17571 var contentCfg = cfg.cn[2];
17574 if(this.indicatorpos == 'right'){
17579 cls : 'control-label col-form-label',
17583 html : this.fieldLabel
17587 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17588 tooltip : 'This field is required'
17593 cls : "roo-combobox-wrap ",
17601 labelCfg = cfg.cn[0];
17602 contentCfg = cfg.cn[1];
17607 if(this.labelWidth > 12){
17608 labelCfg.style = "width: " + this.labelWidth + 'px';
17611 if(this.labelWidth < 13 && this.labelmd == 0){
17612 this.labelmd = this.labelWidth;
17615 if(this.labellg > 0){
17616 labelCfg.cls += ' col-lg-' + this.labellg;
17617 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17620 if(this.labelmd > 0){
17621 labelCfg.cls += ' col-md-' + this.labelmd;
17622 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17625 if(this.labelsm > 0){
17626 labelCfg.cls += ' col-sm-' + this.labelsm;
17627 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17630 if(this.labelxs > 0){
17631 labelCfg.cls += ' col-xs-' + this.labelxs;
17632 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17636 } else if ( this.fieldLabel.length) {
17640 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17641 tooltip : 'This field is required'
17645 cls : 'control-label',
17646 html : this.fieldLabel
17657 if(this.indicatorpos == 'right'){
17661 cls : 'control-label',
17662 html : this.fieldLabel,
17666 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17667 tooltip : 'This field is required'
17684 var settings = this;
17686 ['xs','sm','md','lg'].map(function(size){
17687 if (settings[size]) {
17688 cfg.cls += ' col-' + size + '-' + settings[size];
17695 initTouchView : function()
17697 this.renderTouchView();
17699 this.touchViewEl.on('scroll', function(){
17700 this.el.dom.scrollTop = 0;
17703 this.originalValue = this.getValue();
17705 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17707 this.inputEl().on("click", this.showTouchView, this);
17708 if (this.triggerEl) {
17709 this.triggerEl.on("click", this.showTouchView, this);
17713 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17714 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17716 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17718 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17719 this.store.on('load', this.onTouchViewLoad, this);
17720 this.store.on('loadexception', this.onTouchViewLoadException, this);
17722 if(this.hiddenName){
17724 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17726 this.hiddenField.dom.value =
17727 this.hiddenValue !== undefined ? this.hiddenValue :
17728 this.value !== undefined ? this.value : '';
17730 this.el.dom.removeAttribute('name');
17731 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17735 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17736 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17739 if(this.removable && !this.multiple){
17740 var close = this.closeTriggerEl();
17742 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17743 close.on('click', this.removeBtnClick, this, close);
17747 * fix the bug in Safari iOS8
17749 this.inputEl().on("focus", function(e){
17750 document.activeElement.blur();
17753 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17760 renderTouchView : function()
17762 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17763 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17765 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17766 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17768 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17769 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17770 this.touchViewBodyEl.setStyle('overflow', 'auto');
17772 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17773 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17775 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17776 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17780 showTouchView : function()
17786 this.touchViewHeaderEl.hide();
17788 if(this.modalTitle.length){
17789 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17790 this.touchViewHeaderEl.show();
17793 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17794 this.touchViewEl.show();
17796 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17798 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17799 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17801 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17803 if(this.modalTitle.length){
17804 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17807 this.touchViewBodyEl.setHeight(bodyHeight);
17811 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17813 this.touchViewEl.addClass(['in','show']);
17816 if(this._touchViewMask){
17817 Roo.get(document.body).addClass("x-body-masked");
17818 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17819 this._touchViewMask.setStyle('z-index', 10000);
17820 this._touchViewMask.addClass('show');
17823 this.doTouchViewQuery();
17827 hideTouchView : function()
17829 this.touchViewEl.removeClass(['in','show']);
17833 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17835 this.touchViewEl.setStyle('display', 'none');
17838 if(this._touchViewMask){
17839 this._touchViewMask.removeClass('show');
17840 Roo.get(document.body).removeClass("x-body-masked");
17844 setTouchViewValue : function()
17851 Roo.each(this.tickItems, function(o){
17856 this.hideTouchView();
17859 doTouchViewQuery : function()
17868 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17872 if(!this.alwaysQuery || this.mode == 'local'){
17873 this.onTouchViewLoad();
17880 onTouchViewBeforeLoad : function(combo,opts)
17886 onTouchViewLoad : function()
17888 if(this.store.getCount() < 1){
17889 this.onTouchViewEmptyResults();
17893 this.clearTouchView();
17895 var rawValue = this.getRawValue();
17897 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17899 this.tickItems = [];
17901 this.store.data.each(function(d, rowIndex){
17902 var row = this.touchViewListGroup.createChild(template);
17904 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17905 row.addClass(d.data.cls);
17908 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17911 html : d.data[this.displayField]
17914 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17915 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17918 row.removeClass('selected');
17919 if(!this.multiple && this.valueField &&
17920 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17923 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17924 row.addClass('selected');
17927 if(this.multiple && this.valueField &&
17928 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17932 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17933 this.tickItems.push(d.data);
17936 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17940 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17942 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17944 if(this.modalTitle.length){
17945 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17948 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17950 if(this.mobile_restrict_height && listHeight < bodyHeight){
17951 this.touchViewBodyEl.setHeight(listHeight);
17956 if(firstChecked && listHeight > bodyHeight){
17957 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17962 onTouchViewLoadException : function()
17964 this.hideTouchView();
17967 onTouchViewEmptyResults : function()
17969 this.clearTouchView();
17971 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17973 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17977 clearTouchView : function()
17979 this.touchViewListGroup.dom.innerHTML = '';
17982 onTouchViewClick : function(e, el, o)
17984 e.preventDefault();
17987 var rowIndex = o.rowIndex;
17989 var r = this.store.getAt(rowIndex);
17991 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17993 if(!this.multiple){
17994 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17995 c.dom.removeAttribute('checked');
17998 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18000 this.setFromData(r.data);
18002 var close = this.closeTriggerEl();
18008 this.hideTouchView();
18010 this.fireEvent('select', this, r, rowIndex);
18015 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18016 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18017 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18021 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18022 this.addItem(r.data);
18023 this.tickItems.push(r.data);
18027 getAutoCreateNativeIOS : function()
18030 cls: 'form-group' //input-group,
18035 cls : 'roo-ios-select'
18039 combobox.name = this.name;
18042 if (this.disabled) {
18043 combobox.disabled = true;
18046 var settings = this;
18048 ['xs','sm','md','lg'].map(function(size){
18049 if (settings[size]) {
18050 cfg.cls += ' col-' + size + '-' + settings[size];
18060 initIOSView : function()
18062 this.store.on('load', this.onIOSViewLoad, this);
18067 onIOSViewLoad : function()
18069 if(this.store.getCount() < 1){
18073 this.clearIOSView();
18075 if(this.allowBlank) {
18077 var default_text = '-- SELECT --';
18079 if(this.placeholder.length){
18080 default_text = this.placeholder;
18083 if(this.emptyTitle.length){
18084 default_text += ' - ' + this.emptyTitle + ' -';
18087 var opt = this.inputEl().createChild({
18090 html : default_text
18094 o[this.valueField] = 0;
18095 o[this.displayField] = default_text;
18097 this.ios_options.push({
18104 this.store.data.each(function(d, rowIndex){
18108 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18109 html = d.data[this.displayField];
18114 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18115 value = d.data[this.valueField];
18124 if(this.value == d.data[this.valueField]){
18125 option['selected'] = true;
18128 var opt = this.inputEl().createChild(option);
18130 this.ios_options.push({
18137 this.inputEl().on('change', function(){
18138 this.fireEvent('select', this);
18143 clearIOSView: function()
18145 this.inputEl().dom.innerHTML = '';
18147 this.ios_options = [];
18150 setIOSValue: function(v)
18154 if(!this.ios_options){
18158 Roo.each(this.ios_options, function(opts){
18160 opts.el.dom.removeAttribute('selected');
18162 if(opts.data[this.valueField] != v){
18166 opts.el.dom.setAttribute('selected', true);
18172 * @cfg {Boolean} grow
18176 * @cfg {Number} growMin
18180 * @cfg {Number} growMax
18189 Roo.apply(Roo.bootstrap.ComboBox, {
18193 cls: 'modal-header',
18215 cls: 'list-group-item',
18219 cls: 'roo-combobox-list-group-item-value'
18223 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18237 listItemCheckbox : {
18239 cls: 'list-group-item',
18243 cls: 'roo-combobox-list-group-item-value'
18247 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18263 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18268 cls: 'modal-footer',
18276 cls: 'col-xs-6 text-left',
18279 cls: 'btn btn-danger roo-touch-view-cancel',
18285 cls: 'col-xs-6 text-right',
18288 cls: 'btn btn-success roo-touch-view-ok',
18299 Roo.apply(Roo.bootstrap.ComboBox, {
18301 touchViewTemplate : {
18303 cls: 'modal fade roo-combobox-touch-view',
18307 cls: 'modal-dialog',
18308 style : 'position:fixed', // we have to fix position....
18312 cls: 'modal-content',
18314 Roo.bootstrap.ComboBox.header,
18315 Roo.bootstrap.ComboBox.body,
18316 Roo.bootstrap.ComboBox.footer
18325 * Ext JS Library 1.1.1
18326 * Copyright(c) 2006-2007, Ext JS, LLC.
18328 * Originally Released Under LGPL - original licence link has changed is not relivant.
18331 * <script type="text/javascript">
18336 * @extends Roo.util.Observable
18337 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18338 * This class also supports single and multi selection modes. <br>
18339 * Create a data model bound view:
18341 var store = new Roo.data.Store(...);
18343 var view = new Roo.View({
18345 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18347 singleSelect: true,
18348 selectedClass: "ydataview-selected",
18352 // listen for node click?
18353 view.on("click", function(vw, index, node, e){
18354 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18358 dataModel.load("foobar.xml");
18360 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18362 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18363 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18365 * Note: old style constructor is still suported (container, template, config)
18368 * Create a new View
18369 * @param {Object} config The config object
18372 Roo.View = function(config, depreciated_tpl, depreciated_config){
18374 this.parent = false;
18376 if (typeof(depreciated_tpl) == 'undefined') {
18377 // new way.. - universal constructor.
18378 Roo.apply(this, config);
18379 this.el = Roo.get(this.el);
18382 this.el = Roo.get(config);
18383 this.tpl = depreciated_tpl;
18384 Roo.apply(this, depreciated_config);
18386 this.wrapEl = this.el.wrap().wrap();
18387 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18390 if(typeof(this.tpl) == "string"){
18391 this.tpl = new Roo.Template(this.tpl);
18393 // support xtype ctors..
18394 this.tpl = new Roo.factory(this.tpl, Roo);
18398 this.tpl.compile();
18403 * @event beforeclick
18404 * Fires before a click is processed. Returns false to cancel the default action.
18405 * @param {Roo.View} this
18406 * @param {Number} index The index of the target node
18407 * @param {HTMLElement} node The target node
18408 * @param {Roo.EventObject} e The raw event object
18410 "beforeclick" : true,
18413 * Fires when a template node is clicked.
18414 * @param {Roo.View} this
18415 * @param {Number} index The index of the target node
18416 * @param {HTMLElement} node The target node
18417 * @param {Roo.EventObject} e The raw event object
18422 * Fires when a template node is double clicked.
18423 * @param {Roo.View} this
18424 * @param {Number} index The index of the target node
18425 * @param {HTMLElement} node The target node
18426 * @param {Roo.EventObject} e The raw event object
18430 * @event contextmenu
18431 * Fires when a template node is right clicked.
18432 * @param {Roo.View} this
18433 * @param {Number} index The index of the target node
18434 * @param {HTMLElement} node The target node
18435 * @param {Roo.EventObject} e The raw event object
18437 "contextmenu" : true,
18439 * @event selectionchange
18440 * Fires when the selected nodes change.
18441 * @param {Roo.View} this
18442 * @param {Array} selections Array of the selected nodes
18444 "selectionchange" : true,
18447 * @event beforeselect
18448 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18449 * @param {Roo.View} this
18450 * @param {HTMLElement} node The node to be selected
18451 * @param {Array} selections Array of currently selected nodes
18453 "beforeselect" : true,
18455 * @event preparedata
18456 * Fires on every row to render, to allow you to change the data.
18457 * @param {Roo.View} this
18458 * @param {Object} data to be rendered (change this)
18460 "preparedata" : true
18468 "click": this.onClick,
18469 "dblclick": this.onDblClick,
18470 "contextmenu": this.onContextMenu,
18474 this.selections = [];
18476 this.cmp = new Roo.CompositeElementLite([]);
18478 this.store = Roo.factory(this.store, Roo.data);
18479 this.setStore(this.store, true);
18482 if ( this.footer && this.footer.xtype) {
18484 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18486 this.footer.dataSource = this.store;
18487 this.footer.container = fctr;
18488 this.footer = Roo.factory(this.footer, Roo);
18489 fctr.insertFirst(this.el);
18491 // this is a bit insane - as the paging toolbar seems to detach the el..
18492 // dom.parentNode.parentNode.parentNode
18493 // they get detached?
18497 Roo.View.superclass.constructor.call(this);
18502 Roo.extend(Roo.View, Roo.util.Observable, {
18505 * @cfg {Roo.data.Store} store Data store to load data from.
18510 * @cfg {String|Roo.Element} el The container element.
18515 * @cfg {String|Roo.Template} tpl The template used by this View
18519 * @cfg {String} dataName the named area of the template to use as the data area
18520 * Works with domtemplates roo-name="name"
18524 * @cfg {String} selectedClass The css class to add to selected nodes
18526 selectedClass : "x-view-selected",
18528 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18533 * @cfg {String} text to display on mask (default Loading)
18537 * @cfg {Boolean} multiSelect Allow multiple selection
18539 multiSelect : false,
18541 * @cfg {Boolean} singleSelect Allow single selection
18543 singleSelect: false,
18546 * @cfg {Boolean} toggleSelect - selecting
18548 toggleSelect : false,
18551 * @cfg {Boolean} tickable - selecting
18556 * Returns the element this view is bound to.
18557 * @return {Roo.Element}
18559 getEl : function(){
18560 return this.wrapEl;
18566 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18568 refresh : function(){
18569 //Roo.log('refresh');
18572 // if we are using something like 'domtemplate', then
18573 // the what gets used is:
18574 // t.applySubtemplate(NAME, data, wrapping data..)
18575 // the outer template then get' applied with
18576 // the store 'extra data'
18577 // and the body get's added to the
18578 // roo-name="data" node?
18579 // <span class='roo-tpl-{name}'></span> ?????
18583 this.clearSelections();
18584 this.el.update("");
18586 var records = this.store.getRange();
18587 if(records.length < 1) {
18589 // is this valid?? = should it render a template??
18591 this.el.update(this.emptyText);
18595 if (this.dataName) {
18596 this.el.update(t.apply(this.store.meta)); //????
18597 el = this.el.child('.roo-tpl-' + this.dataName);
18600 for(var i = 0, len = records.length; i < len; i++){
18601 var data = this.prepareData(records[i].data, i, records[i]);
18602 this.fireEvent("preparedata", this, data, i, records[i]);
18604 var d = Roo.apply({}, data);
18607 Roo.apply(d, {'roo-id' : Roo.id()});
18611 Roo.each(this.parent.item, function(item){
18612 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18615 Roo.apply(d, {'roo-data-checked' : 'checked'});
18619 html[html.length] = Roo.util.Format.trim(
18621 t.applySubtemplate(this.dataName, d, this.store.meta) :
18628 el.update(html.join(""));
18629 this.nodes = el.dom.childNodes;
18630 this.updateIndexes(0);
18635 * Function to override to reformat the data that is sent to
18636 * the template for each node.
18637 * DEPRICATED - use the preparedata event handler.
18638 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18639 * a JSON object for an UpdateManager bound view).
18641 prepareData : function(data, index, record)
18643 this.fireEvent("preparedata", this, data, index, record);
18647 onUpdate : function(ds, record){
18648 // Roo.log('on update');
18649 this.clearSelections();
18650 var index = this.store.indexOf(record);
18651 var n = this.nodes[index];
18652 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18653 n.parentNode.removeChild(n);
18654 this.updateIndexes(index, index);
18660 onAdd : function(ds, records, index)
18662 //Roo.log(['on Add', ds, records, index] );
18663 this.clearSelections();
18664 if(this.nodes.length == 0){
18668 var n = this.nodes[index];
18669 for(var i = 0, len = records.length; i < len; i++){
18670 var d = this.prepareData(records[i].data, i, records[i]);
18672 this.tpl.insertBefore(n, d);
18675 this.tpl.append(this.el, d);
18678 this.updateIndexes(index);
18681 onRemove : function(ds, record, index){
18682 // Roo.log('onRemove');
18683 this.clearSelections();
18684 var el = this.dataName ?
18685 this.el.child('.roo-tpl-' + this.dataName) :
18688 el.dom.removeChild(this.nodes[index]);
18689 this.updateIndexes(index);
18693 * Refresh an individual node.
18694 * @param {Number} index
18696 refreshNode : function(index){
18697 this.onUpdate(this.store, this.store.getAt(index));
18700 updateIndexes : function(startIndex, endIndex){
18701 var ns = this.nodes;
18702 startIndex = startIndex || 0;
18703 endIndex = endIndex || ns.length - 1;
18704 for(var i = startIndex; i <= endIndex; i++){
18705 ns[i].nodeIndex = i;
18710 * Changes the data store this view uses and refresh the view.
18711 * @param {Store} store
18713 setStore : function(store, initial){
18714 if(!initial && this.store){
18715 this.store.un("datachanged", this.refresh);
18716 this.store.un("add", this.onAdd);
18717 this.store.un("remove", this.onRemove);
18718 this.store.un("update", this.onUpdate);
18719 this.store.un("clear", this.refresh);
18720 this.store.un("beforeload", this.onBeforeLoad);
18721 this.store.un("load", this.onLoad);
18722 this.store.un("loadexception", this.onLoad);
18726 store.on("datachanged", this.refresh, this);
18727 store.on("add", this.onAdd, this);
18728 store.on("remove", this.onRemove, this);
18729 store.on("update", this.onUpdate, this);
18730 store.on("clear", this.refresh, this);
18731 store.on("beforeload", this.onBeforeLoad, this);
18732 store.on("load", this.onLoad, this);
18733 store.on("loadexception", this.onLoad, this);
18741 * onbeforeLoad - masks the loading area.
18744 onBeforeLoad : function(store,opts)
18746 //Roo.log('onBeforeLoad');
18748 this.el.update("");
18750 this.el.mask(this.mask ? this.mask : "Loading" );
18752 onLoad : function ()
18759 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18760 * @param {HTMLElement} node
18761 * @return {HTMLElement} The template node
18763 findItemFromChild : function(node){
18764 var el = this.dataName ?
18765 this.el.child('.roo-tpl-' + this.dataName,true) :
18768 if(!node || node.parentNode == el){
18771 var p = node.parentNode;
18772 while(p && p != el){
18773 if(p.parentNode == el){
18782 onClick : function(e){
18783 var item = this.findItemFromChild(e.getTarget());
18785 var index = this.indexOf(item);
18786 if(this.onItemClick(item, index, e) !== false){
18787 this.fireEvent("click", this, index, item, e);
18790 this.clearSelections();
18795 onContextMenu : function(e){
18796 var item = this.findItemFromChild(e.getTarget());
18798 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18803 onDblClick : function(e){
18804 var item = this.findItemFromChild(e.getTarget());
18806 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18810 onItemClick : function(item, index, e)
18812 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18815 if (this.toggleSelect) {
18816 var m = this.isSelected(item) ? 'unselect' : 'select';
18819 _t[m](item, true, false);
18822 if(this.multiSelect || this.singleSelect){
18823 if(this.multiSelect && e.shiftKey && this.lastSelection){
18824 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18826 this.select(item, this.multiSelect && e.ctrlKey);
18827 this.lastSelection = item;
18830 if(!this.tickable){
18831 e.preventDefault();
18839 * Get the number of selected nodes.
18842 getSelectionCount : function(){
18843 return this.selections.length;
18847 * Get the currently selected nodes.
18848 * @return {Array} An array of HTMLElements
18850 getSelectedNodes : function(){
18851 return this.selections;
18855 * Get the indexes of the selected nodes.
18858 getSelectedIndexes : function(){
18859 var indexes = [], s = this.selections;
18860 for(var i = 0, len = s.length; i < len; i++){
18861 indexes.push(s[i].nodeIndex);
18867 * Clear all selections
18868 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18870 clearSelections : function(suppressEvent){
18871 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18872 this.cmp.elements = this.selections;
18873 this.cmp.removeClass(this.selectedClass);
18874 this.selections = [];
18875 if(!suppressEvent){
18876 this.fireEvent("selectionchange", this, this.selections);
18882 * Returns true if the passed node is selected
18883 * @param {HTMLElement/Number} node The node or node index
18884 * @return {Boolean}
18886 isSelected : function(node){
18887 var s = this.selections;
18891 node = this.getNode(node);
18892 return s.indexOf(node) !== -1;
18897 * @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
18898 * @param {Boolean} keepExisting (optional) true to keep existing selections
18899 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18901 select : function(nodeInfo, keepExisting, suppressEvent){
18902 if(nodeInfo instanceof Array){
18904 this.clearSelections(true);
18906 for(var i = 0, len = nodeInfo.length; i < len; i++){
18907 this.select(nodeInfo[i], true, true);
18911 var node = this.getNode(nodeInfo);
18912 if(!node || this.isSelected(node)){
18913 return; // already selected.
18916 this.clearSelections(true);
18919 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18920 Roo.fly(node).addClass(this.selectedClass);
18921 this.selections.push(node);
18922 if(!suppressEvent){
18923 this.fireEvent("selectionchange", this, this.selections);
18931 * @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
18932 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18933 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18935 unselect : function(nodeInfo, keepExisting, suppressEvent)
18937 if(nodeInfo instanceof Array){
18938 Roo.each(this.selections, function(s) {
18939 this.unselect(s, nodeInfo);
18943 var node = this.getNode(nodeInfo);
18944 if(!node || !this.isSelected(node)){
18945 //Roo.log("not selected");
18946 return; // not selected.
18950 Roo.each(this.selections, function(s) {
18952 Roo.fly(node).removeClass(this.selectedClass);
18959 this.selections= ns;
18960 this.fireEvent("selectionchange", this, this.selections);
18964 * Gets a template node.
18965 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18966 * @return {HTMLElement} The node or null if it wasn't found
18968 getNode : function(nodeInfo){
18969 if(typeof nodeInfo == "string"){
18970 return document.getElementById(nodeInfo);
18971 }else if(typeof nodeInfo == "number"){
18972 return this.nodes[nodeInfo];
18978 * Gets a range template nodes.
18979 * @param {Number} startIndex
18980 * @param {Number} endIndex
18981 * @return {Array} An array of nodes
18983 getNodes : function(start, end){
18984 var ns = this.nodes;
18985 start = start || 0;
18986 end = typeof end == "undefined" ? ns.length - 1 : end;
18989 for(var i = start; i <= end; i++){
18993 for(var i = start; i >= end; i--){
19001 * Finds the index of the passed node
19002 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19003 * @return {Number} The index of the node or -1
19005 indexOf : function(node){
19006 node = this.getNode(node);
19007 if(typeof node.nodeIndex == "number"){
19008 return node.nodeIndex;
19010 var ns = this.nodes;
19011 for(var i = 0, len = ns.length; i < len; i++){
19022 * based on jquery fullcalendar
19026 Roo.bootstrap = Roo.bootstrap || {};
19028 * @class Roo.bootstrap.Calendar
19029 * @extends Roo.bootstrap.Component
19030 * Bootstrap Calendar class
19031 * @cfg {Boolean} loadMask (true|false) default false
19032 * @cfg {Object} header generate the user specific header of the calendar, default false
19035 * Create a new Container
19036 * @param {Object} config The config object
19041 Roo.bootstrap.Calendar = function(config){
19042 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19046 * Fires when a date is selected
19047 * @param {DatePicker} this
19048 * @param {Date} date The selected date
19052 * @event monthchange
19053 * Fires when the displayed month changes
19054 * @param {DatePicker} this
19055 * @param {Date} date The selected month
19057 'monthchange': true,
19059 * @event evententer
19060 * Fires when mouse over an event
19061 * @param {Calendar} this
19062 * @param {event} Event
19064 'evententer': true,
19066 * @event eventleave
19067 * Fires when the mouse leaves an
19068 * @param {Calendar} this
19071 'eventleave': true,
19073 * @event eventclick
19074 * Fires when the mouse click an
19075 * @param {Calendar} this
19084 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19087 * @cfg {Number} startDay
19088 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19096 getAutoCreate : function(){
19099 var fc_button = function(name, corner, style, content ) {
19100 return Roo.apply({},{
19102 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19104 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19107 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19118 style : 'width:100%',
19125 cls : 'fc-header-left',
19127 fc_button('prev', 'left', 'arrow', '‹' ),
19128 fc_button('next', 'right', 'arrow', '›' ),
19129 { tag: 'span', cls: 'fc-header-space' },
19130 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19138 cls : 'fc-header-center',
19142 cls: 'fc-header-title',
19145 html : 'month / year'
19153 cls : 'fc-header-right',
19155 /* fc_button('month', 'left', '', 'month' ),
19156 fc_button('week', '', '', 'week' ),
19157 fc_button('day', 'right', '', 'day' )
19169 header = this.header;
19172 var cal_heads = function() {
19174 // fixme - handle this.
19176 for (var i =0; i < Date.dayNames.length; i++) {
19177 var d = Date.dayNames[i];
19180 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19181 html : d.substring(0,3)
19185 ret[0].cls += ' fc-first';
19186 ret[6].cls += ' fc-last';
19189 var cal_cell = function(n) {
19192 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19197 cls: 'fc-day-number',
19201 cls: 'fc-day-content',
19205 style: 'position: relative;' // height: 17px;
19217 var cal_rows = function() {
19220 for (var r = 0; r < 6; r++) {
19227 for (var i =0; i < Date.dayNames.length; i++) {
19228 var d = Date.dayNames[i];
19229 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19232 row.cn[0].cls+=' fc-first';
19233 row.cn[0].cn[0].style = 'min-height:90px';
19234 row.cn[6].cls+=' fc-last';
19238 ret[0].cls += ' fc-first';
19239 ret[4].cls += ' fc-prev-last';
19240 ret[5].cls += ' fc-last';
19247 cls: 'fc-border-separate',
19248 style : 'width:100%',
19256 cls : 'fc-first fc-last',
19274 cls : 'fc-content',
19275 style : "position: relative;",
19278 cls : 'fc-view fc-view-month fc-grid',
19279 style : 'position: relative',
19280 unselectable : 'on',
19283 cls : 'fc-event-container',
19284 style : 'position:absolute;z-index:8;top:0;left:0;'
19302 initEvents : function()
19305 throw "can not find store for calendar";
19311 style: "text-align:center",
19315 style: "background-color:white;width:50%;margin:250 auto",
19319 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19330 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19332 var size = this.el.select('.fc-content', true).first().getSize();
19333 this.maskEl.setSize(size.width, size.height);
19334 this.maskEl.enableDisplayMode("block");
19335 if(!this.loadMask){
19336 this.maskEl.hide();
19339 this.store = Roo.factory(this.store, Roo.data);
19340 this.store.on('load', this.onLoad, this);
19341 this.store.on('beforeload', this.onBeforeLoad, this);
19345 this.cells = this.el.select('.fc-day',true);
19346 //Roo.log(this.cells);
19347 this.textNodes = this.el.query('.fc-day-number');
19348 this.cells.addClassOnOver('fc-state-hover');
19350 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19351 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19352 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19353 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19355 this.on('monthchange', this.onMonthChange, this);
19357 this.update(new Date().clearTime());
19360 resize : function() {
19361 var sz = this.el.getSize();
19363 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19364 this.el.select('.fc-day-content div',true).setHeight(34);
19369 showPrevMonth : function(e){
19370 this.update(this.activeDate.add("mo", -1));
19372 showToday : function(e){
19373 this.update(new Date().clearTime());
19376 showNextMonth : function(e){
19377 this.update(this.activeDate.add("mo", 1));
19381 showPrevYear : function(){
19382 this.update(this.activeDate.add("y", -1));
19386 showNextYear : function(){
19387 this.update(this.activeDate.add("y", 1));
19392 update : function(date)
19394 var vd = this.activeDate;
19395 this.activeDate = date;
19396 // if(vd && this.el){
19397 // var t = date.getTime();
19398 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19399 // Roo.log('using add remove');
19401 // this.fireEvent('monthchange', this, date);
19403 // this.cells.removeClass("fc-state-highlight");
19404 // this.cells.each(function(c){
19405 // if(c.dateValue == t){
19406 // c.addClass("fc-state-highlight");
19407 // setTimeout(function(){
19408 // try{c.dom.firstChild.focus();}catch(e){}
19418 var days = date.getDaysInMonth();
19420 var firstOfMonth = date.getFirstDateOfMonth();
19421 var startingPos = firstOfMonth.getDay()-this.startDay;
19423 if(startingPos < this.startDay){
19427 var pm = date.add(Date.MONTH, -1);
19428 var prevStart = pm.getDaysInMonth()-startingPos;
19430 this.cells = this.el.select('.fc-day',true);
19431 this.textNodes = this.el.query('.fc-day-number');
19432 this.cells.addClassOnOver('fc-state-hover');
19434 var cells = this.cells.elements;
19435 var textEls = this.textNodes;
19437 Roo.each(cells, function(cell){
19438 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19441 days += startingPos;
19443 // convert everything to numbers so it's fast
19444 var day = 86400000;
19445 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19448 //Roo.log(prevStart);
19450 var today = new Date().clearTime().getTime();
19451 var sel = date.clearTime().getTime();
19452 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19453 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19454 var ddMatch = this.disabledDatesRE;
19455 var ddText = this.disabledDatesText;
19456 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19457 var ddaysText = this.disabledDaysText;
19458 var format = this.format;
19460 var setCellClass = function(cal, cell){
19464 //Roo.log('set Cell Class');
19466 var t = d.getTime();
19470 cell.dateValue = t;
19472 cell.className += " fc-today";
19473 cell.className += " fc-state-highlight";
19474 cell.title = cal.todayText;
19477 // disable highlight in other month..
19478 //cell.className += " fc-state-highlight";
19483 cell.className = " fc-state-disabled";
19484 cell.title = cal.minText;
19488 cell.className = " fc-state-disabled";
19489 cell.title = cal.maxText;
19493 if(ddays.indexOf(d.getDay()) != -1){
19494 cell.title = ddaysText;
19495 cell.className = " fc-state-disabled";
19498 if(ddMatch && format){
19499 var fvalue = d.dateFormat(format);
19500 if(ddMatch.test(fvalue)){
19501 cell.title = ddText.replace("%0", fvalue);
19502 cell.className = " fc-state-disabled";
19506 if (!cell.initialClassName) {
19507 cell.initialClassName = cell.dom.className;
19510 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19515 for(; i < startingPos; i++) {
19516 textEls[i].innerHTML = (++prevStart);
19517 d.setDate(d.getDate()+1);
19519 cells[i].className = "fc-past fc-other-month";
19520 setCellClass(this, cells[i]);
19525 for(; i < days; i++){
19526 intDay = i - startingPos + 1;
19527 textEls[i].innerHTML = (intDay);
19528 d.setDate(d.getDate()+1);
19530 cells[i].className = ''; // "x-date-active";
19531 setCellClass(this, cells[i]);
19535 for(; i < 42; i++) {
19536 textEls[i].innerHTML = (++extraDays);
19537 d.setDate(d.getDate()+1);
19539 cells[i].className = "fc-future fc-other-month";
19540 setCellClass(this, cells[i]);
19543 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19545 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19547 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19548 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19550 if(totalRows != 6){
19551 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19552 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19555 this.fireEvent('monthchange', this, date);
19559 if(!this.internalRender){
19560 var main = this.el.dom.firstChild;
19561 var w = main.offsetWidth;
19562 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19563 Roo.fly(main).setWidth(w);
19564 this.internalRender = true;
19565 // opera does not respect the auto grow header center column
19566 // then, after it gets a width opera refuses to recalculate
19567 // without a second pass
19568 if(Roo.isOpera && !this.secondPass){
19569 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19570 this.secondPass = true;
19571 this.update.defer(10, this, [date]);
19578 findCell : function(dt) {
19579 dt = dt.clearTime().getTime();
19581 this.cells.each(function(c){
19582 //Roo.log("check " +c.dateValue + '?=' + dt);
19583 if(c.dateValue == dt){
19593 findCells : function(ev) {
19594 var s = ev.start.clone().clearTime().getTime();
19596 var e= ev.end.clone().clearTime().getTime();
19599 this.cells.each(function(c){
19600 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19602 if(c.dateValue > e){
19605 if(c.dateValue < s){
19614 // findBestRow: function(cells)
19618 // for (var i =0 ; i < cells.length;i++) {
19619 // ret = Math.max(cells[i].rows || 0,ret);
19626 addItem : function(ev)
19628 // look for vertical location slot in
19629 var cells = this.findCells(ev);
19631 // ev.row = this.findBestRow(cells);
19633 // work out the location.
19637 for(var i =0; i < cells.length; i++) {
19639 cells[i].row = cells[0].row;
19642 cells[i].row = cells[i].row + 1;
19652 if (crow.start.getY() == cells[i].getY()) {
19654 crow.end = cells[i];
19671 cells[0].events.push(ev);
19673 this.calevents.push(ev);
19676 clearEvents: function() {
19678 if(!this.calevents){
19682 Roo.each(this.cells.elements, function(c){
19688 Roo.each(this.calevents, function(e) {
19689 Roo.each(e.els, function(el) {
19690 el.un('mouseenter' ,this.onEventEnter, this);
19691 el.un('mouseleave' ,this.onEventLeave, this);
19696 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19702 renderEvents: function()
19706 this.cells.each(function(c) {
19715 if(c.row != c.events.length){
19716 r = 4 - (4 - (c.row - c.events.length));
19719 c.events = ev.slice(0, r);
19720 c.more = ev.slice(r);
19722 if(c.more.length && c.more.length == 1){
19723 c.events.push(c.more.pop());
19726 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19730 this.cells.each(function(c) {
19732 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19735 for (var e = 0; e < c.events.length; e++){
19736 var ev = c.events[e];
19737 var rows = ev.rows;
19739 for(var i = 0; i < rows.length; i++) {
19741 // how many rows should it span..
19744 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19745 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19747 unselectable : "on",
19750 cls: 'fc-event-inner',
19754 // cls: 'fc-event-time',
19755 // html : cells.length > 1 ? '' : ev.time
19759 cls: 'fc-event-title',
19760 html : String.format('{0}', ev.title)
19767 cls: 'ui-resizable-handle ui-resizable-e',
19768 html : '  '
19775 cfg.cls += ' fc-event-start';
19777 if ((i+1) == rows.length) {
19778 cfg.cls += ' fc-event-end';
19781 var ctr = _this.el.select('.fc-event-container',true).first();
19782 var cg = ctr.createChild(cfg);
19784 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19785 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19787 var r = (c.more.length) ? 1 : 0;
19788 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19789 cg.setWidth(ebox.right - sbox.x -2);
19791 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19792 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19793 cg.on('click', _this.onEventClick, _this, ev);
19804 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19805 style : 'position: absolute',
19806 unselectable : "on",
19809 cls: 'fc-event-inner',
19813 cls: 'fc-event-title',
19821 cls: 'ui-resizable-handle ui-resizable-e',
19822 html : '  '
19828 var ctr = _this.el.select('.fc-event-container',true).first();
19829 var cg = ctr.createChild(cfg);
19831 var sbox = c.select('.fc-day-content',true).first().getBox();
19832 var ebox = c.select('.fc-day-content',true).first().getBox();
19834 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19835 cg.setWidth(ebox.right - sbox.x -2);
19837 cg.on('click', _this.onMoreEventClick, _this, c.more);
19847 onEventEnter: function (e, el,event,d) {
19848 this.fireEvent('evententer', this, el, event);
19851 onEventLeave: function (e, el,event,d) {
19852 this.fireEvent('eventleave', this, el, event);
19855 onEventClick: function (e, el,event,d) {
19856 this.fireEvent('eventclick', this, el, event);
19859 onMonthChange: function () {
19863 onMoreEventClick: function(e, el, more)
19867 this.calpopover.placement = 'right';
19868 this.calpopover.setTitle('More');
19870 this.calpopover.setContent('');
19872 var ctr = this.calpopover.el.select('.popover-content', true).first();
19874 Roo.each(more, function(m){
19876 cls : 'fc-event-hori fc-event-draggable',
19879 var cg = ctr.createChild(cfg);
19881 cg.on('click', _this.onEventClick, _this, m);
19884 this.calpopover.show(el);
19889 onLoad: function ()
19891 this.calevents = [];
19894 if(this.store.getCount() > 0){
19895 this.store.data.each(function(d){
19898 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19899 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19900 time : d.data.start_time,
19901 title : d.data.title,
19902 description : d.data.description,
19903 venue : d.data.venue
19908 this.renderEvents();
19910 if(this.calevents.length && this.loadMask){
19911 this.maskEl.hide();
19915 onBeforeLoad: function()
19917 this.clearEvents();
19919 this.maskEl.show();
19933 * @class Roo.bootstrap.Popover
19934 * @extends Roo.bootstrap.Component
19935 * Bootstrap Popover class
19936 * @cfg {String} html contents of the popover (or false to use children..)
19937 * @cfg {String} title of popover (or false to hide)
19938 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19939 * @cfg {String} trigger click || hover (or false to trigger manually)
19940 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19941 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19942 * - if false and it has a 'parent' then it will be automatically added to that element
19943 * - if string - Roo.get will be called
19944 * @cfg {Number} delay - delay before showing
19947 * Create a new Popover
19948 * @param {Object} config The config object
19951 Roo.bootstrap.Popover = function(config){
19952 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19958 * After the popover show
19960 * @param {Roo.bootstrap.Popover} this
19965 * After the popover hide
19967 * @param {Roo.bootstrap.Popover} this
19973 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19978 placement : 'right',
19979 trigger : 'hover', // hover
19985 can_build_overlaid : false,
19987 maskEl : false, // the mask element
19990 alignEl : false, // when show is called with an element - this get's stored.
19992 getChildContainer : function()
19994 return this.contentEl;
19997 getPopoverHeader : function()
19999 this.title = true; // flag not to hide it..
20000 this.headerEl.addClass('p-0');
20001 return this.headerEl
20005 getAutoCreate : function(){
20008 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20009 style: 'display:block',
20015 cls : 'popover-inner ',
20019 cls: 'popover-title popover-header',
20020 html : this.title === false ? '' : this.title
20023 cls : 'popover-content popover-body ' + (this.cls || ''),
20024 html : this.html || ''
20035 * @param {string} the title
20037 setTitle: function(str)
20041 this.headerEl.dom.innerHTML = str;
20046 * @param {string} the body content
20048 setContent: function(str)
20051 if (this.contentEl) {
20052 this.contentEl.dom.innerHTML = str;
20056 // as it get's added to the bottom of the page.
20057 onRender : function(ct, position)
20059 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20064 var cfg = Roo.apply({}, this.getAutoCreate());
20068 cfg.cls += ' ' + this.cls;
20071 cfg.style = this.style;
20073 //Roo.log("adding to ");
20074 this.el = Roo.get(document.body).createChild(cfg, position);
20075 // Roo.log(this.el);
20078 this.contentEl = this.el.select('.popover-content',true).first();
20079 this.headerEl = this.el.select('.popover-title',true).first();
20082 if(typeof(this.items) != 'undefined'){
20083 var items = this.items;
20086 for(var i =0;i < items.length;i++) {
20087 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20091 this.items = nitems;
20093 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20094 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20101 resizeMask : function()
20103 this.maskEl.setSize(
20104 Roo.lib.Dom.getViewWidth(true),
20105 Roo.lib.Dom.getViewHeight(true)
20109 initEvents : function()
20113 Roo.bootstrap.Popover.register(this);
20116 this.arrowEl = this.el.select('.arrow',true).first();
20117 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20118 this.el.enableDisplayMode('block');
20122 if (this.over === false && !this.parent()) {
20125 if (this.triggers === false) {
20130 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20131 var triggers = this.trigger ? this.trigger.split(' ') : [];
20132 Roo.each(triggers, function(trigger) {
20134 if (trigger == 'click') {
20135 on_el.on('click', this.toggle, this);
20136 } else if (trigger != 'manual') {
20137 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20138 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20140 on_el.on(eventIn ,this.enter, this);
20141 on_el.on(eventOut, this.leave, this);
20151 toggle : function () {
20152 this.hoverState == 'in' ? this.leave() : this.enter();
20155 enter : function () {
20157 clearTimeout(this.timeout);
20159 this.hoverState = 'in';
20161 if (!this.delay || !this.delay.show) {
20166 this.timeout = setTimeout(function () {
20167 if (_t.hoverState == 'in') {
20170 }, this.delay.show)
20173 leave : function() {
20174 clearTimeout(this.timeout);
20176 this.hoverState = 'out';
20178 if (!this.delay || !this.delay.hide) {
20183 this.timeout = setTimeout(function () {
20184 if (_t.hoverState == 'out') {
20187 }, this.delay.hide)
20191 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20192 * @param {string} (left|right|top|bottom) position
20194 show : function (on_el, placement)
20196 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20197 on_el = on_el || false; // default to false
20200 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20201 on_el = this.parent().el;
20202 } else if (this.over) {
20203 Roo.get(this.over);
20208 this.alignEl = Roo.get( on_el );
20211 this.render(document.body);
20217 if (this.title === false) {
20218 this.headerEl.hide();
20223 this.el.dom.style.display = 'block';
20226 if (this.alignEl) {
20227 this.updatePosition(this.placement, true);
20230 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20231 var es = this.el.getSize();
20232 var x = Roo.lib.Dom.getViewWidth()/2;
20233 var y = Roo.lib.Dom.getViewHeight()/2;
20234 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20239 //var arrow = this.el.select('.arrow',true).first();
20240 //arrow.set(align[2],
20242 this.el.addClass('in');
20246 this.hoverState = 'in';
20249 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20250 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20251 this.maskEl.dom.style.display = 'block';
20252 this.maskEl.addClass('show');
20254 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20256 this.fireEvent('show', this);
20260 * fire this manually after loading a grid in the table for example
20261 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20262 * @param {Boolean} try and move it if we cant get right position.
20264 updatePosition : function(placement, try_move)
20266 // allow for calling with no parameters
20267 placement = placement ? placement : this.placement;
20268 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20270 this.el.removeClass([
20271 'fade','top','bottom', 'left', 'right','in',
20272 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20274 this.el.addClass(placement + ' bs-popover-' + placement);
20276 if (!this.alignEl ) {
20280 switch (placement) {
20282 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20283 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20284 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20285 //normal display... or moved up/down.
20286 this.el.setXY(offset);
20287 var xy = this.alignEl.getAnchorXY('tr', false);
20289 this.arrowEl.setXY(xy);
20292 // continue through...
20293 return this.updatePosition('left', false);
20297 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20298 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20299 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20300 //normal display... or moved up/down.
20301 this.el.setXY(offset);
20302 var xy = this.alignEl.getAnchorXY('tl', false);
20303 xy[0]-=10;xy[1]+=5; // << fix me
20304 this.arrowEl.setXY(xy);
20308 return this.updatePosition('right', false);
20311 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20312 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20313 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20314 //normal display... or moved up/down.
20315 this.el.setXY(offset);
20316 var xy = this.alignEl.getAnchorXY('t', false);
20317 xy[1]-=10; // << fix me
20318 this.arrowEl.setXY(xy);
20322 return this.updatePosition('bottom', false);
20325 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20326 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20327 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20328 //normal display... or moved up/down.
20329 this.el.setXY(offset);
20330 var xy = this.alignEl.getAnchorXY('b', false);
20331 xy[1]+=2; // << fix me
20332 this.arrowEl.setXY(xy);
20336 return this.updatePosition('top', false);
20347 this.el.setXY([0,0]);
20348 this.el.removeClass('in');
20350 this.hoverState = null;
20351 this.maskEl.hide(); // always..
20352 this.fireEvent('hide', this);
20358 Roo.apply(Roo.bootstrap.Popover, {
20361 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20362 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20363 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20364 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20369 clickHander : false,
20372 onMouseDown : function(e)
20374 if (!e.getTarget(".roo-popover")) {
20382 register : function(popup)
20384 if (!Roo.bootstrap.Popover.clickHandler) {
20385 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20387 // hide other popups.
20389 this.popups.push(popup);
20391 hideAll : function()
20393 this.popups.forEach(function(p) {
20401 * Card header - holder for the card header elements.
20406 * @class Roo.bootstrap.PopoverNav
20407 * @extends Roo.bootstrap.NavGroup
20408 * Bootstrap Popover header navigation class
20410 * Create a new Popover Header Navigation
20411 * @param {Object} config The config object
20414 Roo.bootstrap.PopoverNav = function(config){
20415 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20418 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20421 container_method : 'getPopoverHeader'
20439 * @class Roo.bootstrap.Progress
20440 * @extends Roo.bootstrap.Component
20441 * Bootstrap Progress class
20442 * @cfg {Boolean} striped striped of the progress bar
20443 * @cfg {Boolean} active animated of the progress bar
20447 * Create a new Progress
20448 * @param {Object} config The config object
20451 Roo.bootstrap.Progress = function(config){
20452 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20455 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20460 getAutoCreate : function(){
20468 cfg.cls += ' progress-striped';
20472 cfg.cls += ' active';
20491 * @class Roo.bootstrap.ProgressBar
20492 * @extends Roo.bootstrap.Component
20493 * Bootstrap ProgressBar class
20494 * @cfg {Number} aria_valuenow aria-value now
20495 * @cfg {Number} aria_valuemin aria-value min
20496 * @cfg {Number} aria_valuemax aria-value max
20497 * @cfg {String} label label for the progress bar
20498 * @cfg {String} panel (success | info | warning | danger )
20499 * @cfg {String} role role of the progress bar
20500 * @cfg {String} sr_only text
20504 * Create a new ProgressBar
20505 * @param {Object} config The config object
20508 Roo.bootstrap.ProgressBar = function(config){
20509 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20512 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20516 aria_valuemax : 100,
20522 getAutoCreate : function()
20527 cls: 'progress-bar',
20528 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20540 cfg.role = this.role;
20543 if(this.aria_valuenow){
20544 cfg['aria-valuenow'] = this.aria_valuenow;
20547 if(this.aria_valuemin){
20548 cfg['aria-valuemin'] = this.aria_valuemin;
20551 if(this.aria_valuemax){
20552 cfg['aria-valuemax'] = this.aria_valuemax;
20555 if(this.label && !this.sr_only){
20556 cfg.html = this.label;
20560 cfg.cls += ' progress-bar-' + this.panel;
20566 update : function(aria_valuenow)
20568 this.aria_valuenow = aria_valuenow;
20570 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20585 * @class Roo.bootstrap.TabGroup
20586 * @extends Roo.bootstrap.Column
20587 * Bootstrap Column class
20588 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20589 * @cfg {Boolean} carousel true to make the group behave like a carousel
20590 * @cfg {Boolean} bullets show bullets for the panels
20591 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20592 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20593 * @cfg {Boolean} showarrow (true|false) show arrow default true
20596 * Create a new TabGroup
20597 * @param {Object} config The config object
20600 Roo.bootstrap.TabGroup = function(config){
20601 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20603 this.navId = Roo.id();
20606 Roo.bootstrap.TabGroup.register(this);
20610 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20613 transition : false,
20618 slideOnTouch : false,
20621 getAutoCreate : function()
20623 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20625 cfg.cls += ' tab-content';
20627 if (this.carousel) {
20628 cfg.cls += ' carousel slide';
20631 cls : 'carousel-inner',
20635 if(this.bullets && !Roo.isTouch){
20638 cls : 'carousel-bullets',
20642 if(this.bullets_cls){
20643 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20650 cfg.cn[0].cn.push(bullets);
20653 if(this.showarrow){
20654 cfg.cn[0].cn.push({
20656 class : 'carousel-arrow',
20660 class : 'carousel-prev',
20664 class : 'fa fa-chevron-left'
20670 class : 'carousel-next',
20674 class : 'fa fa-chevron-right'
20687 initEvents: function()
20689 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20690 // this.el.on("touchstart", this.onTouchStart, this);
20693 if(this.autoslide){
20696 this.slideFn = window.setInterval(function() {
20697 _this.showPanelNext();
20701 if(this.showarrow){
20702 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20703 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20709 // onTouchStart : function(e, el, o)
20711 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20715 // this.showPanelNext();
20719 getChildContainer : function()
20721 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20725 * register a Navigation item
20726 * @param {Roo.bootstrap.NavItem} the navitem to add
20728 register : function(item)
20730 this.tabs.push( item);
20731 item.navId = this.navId; // not really needed..
20736 getActivePanel : function()
20739 Roo.each(this.tabs, function(t) {
20749 getPanelByName : function(n)
20752 Roo.each(this.tabs, function(t) {
20753 if (t.tabId == n) {
20761 indexOfPanel : function(p)
20764 Roo.each(this.tabs, function(t,i) {
20765 if (t.tabId == p.tabId) {
20774 * show a specific panel
20775 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20776 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20778 showPanel : function (pan)
20780 if(this.transition || typeof(pan) == 'undefined'){
20781 Roo.log("waiting for the transitionend");
20785 if (typeof(pan) == 'number') {
20786 pan = this.tabs[pan];
20789 if (typeof(pan) == 'string') {
20790 pan = this.getPanelByName(pan);
20793 var cur = this.getActivePanel();
20796 Roo.log('pan or acitve pan is undefined');
20800 if (pan.tabId == this.getActivePanel().tabId) {
20804 if (false === cur.fireEvent('beforedeactivate')) {
20808 if(this.bullets > 0 && !Roo.isTouch){
20809 this.setActiveBullet(this.indexOfPanel(pan));
20812 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20814 //class="carousel-item carousel-item-next carousel-item-left"
20816 this.transition = true;
20817 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20818 var lr = dir == 'next' ? 'left' : 'right';
20819 pan.el.addClass(dir); // or prev
20820 pan.el.addClass('carousel-item-' + dir); // or prev
20821 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20822 cur.el.addClass(lr); // or right
20823 pan.el.addClass(lr);
20824 cur.el.addClass('carousel-item-' +lr); // or right
20825 pan.el.addClass('carousel-item-' +lr);
20829 cur.el.on('transitionend', function() {
20830 Roo.log("trans end?");
20832 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20833 pan.setActive(true);
20835 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20836 cur.setActive(false);
20838 _this.transition = false;
20840 }, this, { single: true } );
20845 cur.setActive(false);
20846 pan.setActive(true);
20851 showPanelNext : function()
20853 var i = this.indexOfPanel(this.getActivePanel());
20855 if (i >= this.tabs.length - 1 && !this.autoslide) {
20859 if (i >= this.tabs.length - 1 && this.autoslide) {
20863 this.showPanel(this.tabs[i+1]);
20866 showPanelPrev : function()
20868 var i = this.indexOfPanel(this.getActivePanel());
20870 if (i < 1 && !this.autoslide) {
20874 if (i < 1 && this.autoslide) {
20875 i = this.tabs.length;
20878 this.showPanel(this.tabs[i-1]);
20882 addBullet: function()
20884 if(!this.bullets || Roo.isTouch){
20887 var ctr = this.el.select('.carousel-bullets',true).first();
20888 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20889 var bullet = ctr.createChild({
20890 cls : 'bullet bullet-' + i
20891 },ctr.dom.lastChild);
20896 bullet.on('click', (function(e, el, o, ii, t){
20898 e.preventDefault();
20900 this.showPanel(ii);
20902 if(this.autoslide && this.slideFn){
20903 clearInterval(this.slideFn);
20904 this.slideFn = window.setInterval(function() {
20905 _this.showPanelNext();
20909 }).createDelegate(this, [i, bullet], true));
20914 setActiveBullet : function(i)
20920 Roo.each(this.el.select('.bullet', true).elements, function(el){
20921 el.removeClass('selected');
20924 var bullet = this.el.select('.bullet-' + i, true).first();
20930 bullet.addClass('selected');
20941 Roo.apply(Roo.bootstrap.TabGroup, {
20945 * register a Navigation Group
20946 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20948 register : function(navgrp)
20950 this.groups[navgrp.navId] = navgrp;
20954 * fetch a Navigation Group based on the navigation ID
20955 * if one does not exist , it will get created.
20956 * @param {string} the navgroup to add
20957 * @returns {Roo.bootstrap.NavGroup} the navgroup
20959 get: function(navId) {
20960 if (typeof(this.groups[navId]) == 'undefined') {
20961 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20963 return this.groups[navId] ;
20978 * @class Roo.bootstrap.TabPanel
20979 * @extends Roo.bootstrap.Component
20980 * Bootstrap TabPanel class
20981 * @cfg {Boolean} active panel active
20982 * @cfg {String} html panel content
20983 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20984 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20985 * @cfg {String} href click to link..
20986 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20990 * Create a new TabPanel
20991 * @param {Object} config The config object
20994 Roo.bootstrap.TabPanel = function(config){
20995 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20999 * Fires when the active status changes
21000 * @param {Roo.bootstrap.TabPanel} this
21001 * @param {Boolean} state the new state
21006 * @event beforedeactivate
21007 * Fires before a tab is de-activated - can be used to do validation on a form.
21008 * @param {Roo.bootstrap.TabPanel} this
21009 * @return {Boolean} false if there is an error
21012 'beforedeactivate': true
21015 this.tabId = this.tabId || Roo.id();
21019 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21026 touchSlide : false,
21027 getAutoCreate : function(){
21032 // item is needed for carousel - not sure if it has any effect otherwise
21033 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21034 html: this.html || ''
21038 cfg.cls += ' active';
21042 cfg.tabId = this.tabId;
21050 initEvents: function()
21052 var p = this.parent();
21054 this.navId = this.navId || p.navId;
21056 if (typeof(this.navId) != 'undefined') {
21057 // not really needed.. but just in case.. parent should be a NavGroup.
21058 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21062 var i = tg.tabs.length - 1;
21064 if(this.active && tg.bullets > 0 && i < tg.bullets){
21065 tg.setActiveBullet(i);
21069 this.el.on('click', this.onClick, this);
21071 if(Roo.isTouch && this.touchSlide){
21072 this.el.on("touchstart", this.onTouchStart, this);
21073 this.el.on("touchmove", this.onTouchMove, this);
21074 this.el.on("touchend", this.onTouchEnd, this);
21079 onRender : function(ct, position)
21081 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21084 setActive : function(state)
21086 Roo.log("panel - set active " + this.tabId + "=" + state);
21088 this.active = state;
21090 this.el.removeClass('active');
21092 } else if (!this.el.hasClass('active')) {
21093 this.el.addClass('active');
21096 this.fireEvent('changed', this, state);
21099 onClick : function(e)
21101 e.preventDefault();
21103 if(!this.href.length){
21107 window.location.href = this.href;
21116 onTouchStart : function(e)
21118 this.swiping = false;
21120 this.startX = e.browserEvent.touches[0].clientX;
21121 this.startY = e.browserEvent.touches[0].clientY;
21124 onTouchMove : function(e)
21126 this.swiping = true;
21128 this.endX = e.browserEvent.touches[0].clientX;
21129 this.endY = e.browserEvent.touches[0].clientY;
21132 onTouchEnd : function(e)
21139 var tabGroup = this.parent();
21141 if(this.endX > this.startX){ // swiping right
21142 tabGroup.showPanelPrev();
21146 if(this.startX > this.endX){ // swiping left
21147 tabGroup.showPanelNext();
21166 * @class Roo.bootstrap.DateField
21167 * @extends Roo.bootstrap.Input
21168 * Bootstrap DateField class
21169 * @cfg {Number} weekStart default 0
21170 * @cfg {String} viewMode default empty, (months|years)
21171 * @cfg {String} minViewMode default empty, (months|years)
21172 * @cfg {Number} startDate default -Infinity
21173 * @cfg {Number} endDate default Infinity
21174 * @cfg {Boolean} todayHighlight default false
21175 * @cfg {Boolean} todayBtn default false
21176 * @cfg {Boolean} calendarWeeks default false
21177 * @cfg {Object} daysOfWeekDisabled default empty
21178 * @cfg {Boolean} singleMode default false (true | false)
21180 * @cfg {Boolean} keyboardNavigation default true
21181 * @cfg {String} language default en
21184 * Create a new DateField
21185 * @param {Object} config The config object
21188 Roo.bootstrap.DateField = function(config){
21189 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21193 * Fires when this field show.
21194 * @param {Roo.bootstrap.DateField} this
21195 * @param {Mixed} date The date value
21200 * Fires when this field hide.
21201 * @param {Roo.bootstrap.DateField} this
21202 * @param {Mixed} date The date value
21207 * Fires when select a date.
21208 * @param {Roo.bootstrap.DateField} this
21209 * @param {Mixed} date The date value
21213 * @event beforeselect
21214 * Fires when before select a date.
21215 * @param {Roo.bootstrap.DateField} this
21216 * @param {Mixed} date The date value
21218 beforeselect : true
21222 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21225 * @cfg {String} format
21226 * The default date format string which can be overriden for localization support. The format must be
21227 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21231 * @cfg {String} altFormats
21232 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21233 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21235 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21243 todayHighlight : false,
21249 keyboardNavigation: true,
21251 calendarWeeks: false,
21253 startDate: -Infinity,
21257 daysOfWeekDisabled: [],
21261 singleMode : false,
21263 UTCDate: function()
21265 return new Date(Date.UTC.apply(Date, arguments));
21268 UTCToday: function()
21270 var today = new Date();
21271 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21274 getDate: function() {
21275 var d = this.getUTCDate();
21276 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21279 getUTCDate: function() {
21283 setDate: function(d) {
21284 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21287 setUTCDate: function(d) {
21289 this.setValue(this.formatDate(this.date));
21292 onRender: function(ct, position)
21295 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21297 this.language = this.language || 'en';
21298 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21299 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21301 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21302 this.format = this.format || 'm/d/y';
21303 this.isInline = false;
21304 this.isInput = true;
21305 this.component = this.el.select('.add-on', true).first() || false;
21306 this.component = (this.component && this.component.length === 0) ? false : this.component;
21307 this.hasInput = this.component && this.inputEl().length;
21309 if (typeof(this.minViewMode === 'string')) {
21310 switch (this.minViewMode) {
21312 this.minViewMode = 1;
21315 this.minViewMode = 2;
21318 this.minViewMode = 0;
21323 if (typeof(this.viewMode === 'string')) {
21324 switch (this.viewMode) {
21337 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21339 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21341 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21343 this.picker().on('mousedown', this.onMousedown, this);
21344 this.picker().on('click', this.onClick, this);
21346 this.picker().addClass('datepicker-dropdown');
21348 this.startViewMode = this.viewMode;
21350 if(this.singleMode){
21351 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21352 v.setVisibilityMode(Roo.Element.DISPLAY);
21356 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21357 v.setStyle('width', '189px');
21361 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21362 if(!this.calendarWeeks){
21367 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21368 v.attr('colspan', function(i, val){
21369 return parseInt(val) + 1;
21374 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21376 this.setStartDate(this.startDate);
21377 this.setEndDate(this.endDate);
21379 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21386 if(this.isInline) {
21391 picker : function()
21393 return this.pickerEl;
21394 // return this.el.select('.datepicker', true).first();
21397 fillDow: function()
21399 var dowCnt = this.weekStart;
21408 if(this.calendarWeeks){
21416 while (dowCnt < this.weekStart + 7) {
21420 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21424 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21427 fillMonths: function()
21430 var months = this.picker().select('>.datepicker-months td', true).first();
21432 months.dom.innerHTML = '';
21438 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21441 months.createChild(month);
21448 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;
21450 if (this.date < this.startDate) {
21451 this.viewDate = new Date(this.startDate);
21452 } else if (this.date > this.endDate) {
21453 this.viewDate = new Date(this.endDate);
21455 this.viewDate = new Date(this.date);
21463 var d = new Date(this.viewDate),
21464 year = d.getUTCFullYear(),
21465 month = d.getUTCMonth(),
21466 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21467 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21468 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21469 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21470 currentDate = this.date && this.date.valueOf(),
21471 today = this.UTCToday();
21473 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21475 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21477 // this.picker.select('>tfoot th.today').
21478 // .text(dates[this.language].today)
21479 // .toggle(this.todayBtn !== false);
21481 this.updateNavArrows();
21484 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21486 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21488 prevMonth.setUTCDate(day);
21490 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21492 var nextMonth = new Date(prevMonth);
21494 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21496 nextMonth = nextMonth.valueOf();
21498 var fillMonths = false;
21500 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21502 while(prevMonth.valueOf() <= nextMonth) {
21505 if (prevMonth.getUTCDay() === this.weekStart) {
21507 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21515 if(this.calendarWeeks){
21516 // ISO 8601: First week contains first thursday.
21517 // ISO also states week starts on Monday, but we can be more abstract here.
21519 // Start of current week: based on weekstart/current date
21520 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21521 // Thursday of this week
21522 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21523 // First Thursday of year, year from thursday
21524 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21525 // Calendar week: ms between thursdays, div ms per day, div 7 days
21526 calWeek = (th - yth) / 864e5 / 7 + 1;
21528 fillMonths.cn.push({
21536 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21538 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21541 if (this.todayHighlight &&
21542 prevMonth.getUTCFullYear() == today.getFullYear() &&
21543 prevMonth.getUTCMonth() == today.getMonth() &&
21544 prevMonth.getUTCDate() == today.getDate()) {
21545 clsName += ' today';
21548 if (currentDate && prevMonth.valueOf() === currentDate) {
21549 clsName += ' active';
21552 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21553 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21554 clsName += ' disabled';
21557 fillMonths.cn.push({
21559 cls: 'day ' + clsName,
21560 html: prevMonth.getDate()
21563 prevMonth.setDate(prevMonth.getDate()+1);
21566 var currentYear = this.date && this.date.getUTCFullYear();
21567 var currentMonth = this.date && this.date.getUTCMonth();
21569 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21571 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21572 v.removeClass('active');
21574 if(currentYear === year && k === currentMonth){
21575 v.addClass('active');
21578 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21579 v.addClass('disabled');
21585 year = parseInt(year/10, 10) * 10;
21587 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21589 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21592 for (var i = -1; i < 11; i++) {
21593 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21595 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21603 showMode: function(dir)
21606 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21609 Roo.each(this.picker().select('>div',true).elements, function(v){
21610 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21613 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21618 if(this.isInline) {
21622 this.picker().removeClass(['bottom', 'top']);
21624 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21626 * place to the top of element!
21630 this.picker().addClass('top');
21631 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21636 this.picker().addClass('bottom');
21638 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21641 parseDate : function(value)
21643 if(!value || value instanceof Date){
21646 var v = Date.parseDate(value, this.format);
21647 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21648 v = Date.parseDate(value, 'Y-m-d');
21650 if(!v && this.altFormats){
21651 if(!this.altFormatsArray){
21652 this.altFormatsArray = this.altFormats.split("|");
21654 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21655 v = Date.parseDate(value, this.altFormatsArray[i]);
21661 formatDate : function(date, fmt)
21663 return (!date || !(date instanceof Date)) ?
21664 date : date.dateFormat(fmt || this.format);
21667 onFocus : function()
21669 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21673 onBlur : function()
21675 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21677 var d = this.inputEl().getValue();
21684 showPopup : function()
21686 this.picker().show();
21690 this.fireEvent('showpopup', this, this.date);
21693 hidePopup : function()
21695 if(this.isInline) {
21698 this.picker().hide();
21699 this.viewMode = this.startViewMode;
21702 this.fireEvent('hidepopup', this, this.date);
21706 onMousedown: function(e)
21708 e.stopPropagation();
21709 e.preventDefault();
21714 Roo.bootstrap.DateField.superclass.keyup.call(this);
21718 setValue: function(v)
21720 if(this.fireEvent('beforeselect', this, v) !== false){
21721 var d = new Date(this.parseDate(v) ).clearTime();
21723 if(isNaN(d.getTime())){
21724 this.date = this.viewDate = '';
21725 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21729 v = this.formatDate(d);
21731 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21733 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21737 this.fireEvent('select', this, this.date);
21741 getValue: function()
21743 return this.formatDate(this.date);
21746 fireKey: function(e)
21748 if (!this.picker().isVisible()){
21749 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21755 var dateChanged = false,
21757 newDate, newViewDate;
21762 e.preventDefault();
21766 if (!this.keyboardNavigation) {
21769 dir = e.keyCode == 37 ? -1 : 1;
21772 newDate = this.moveYear(this.date, dir);
21773 newViewDate = this.moveYear(this.viewDate, dir);
21774 } else if (e.shiftKey){
21775 newDate = this.moveMonth(this.date, dir);
21776 newViewDate = this.moveMonth(this.viewDate, dir);
21778 newDate = new Date(this.date);
21779 newDate.setUTCDate(this.date.getUTCDate() + dir);
21780 newViewDate = new Date(this.viewDate);
21781 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21783 if (this.dateWithinRange(newDate)){
21784 this.date = newDate;
21785 this.viewDate = newViewDate;
21786 this.setValue(this.formatDate(this.date));
21788 e.preventDefault();
21789 dateChanged = true;
21794 if (!this.keyboardNavigation) {
21797 dir = e.keyCode == 38 ? -1 : 1;
21799 newDate = this.moveYear(this.date, dir);
21800 newViewDate = this.moveYear(this.viewDate, dir);
21801 } else if (e.shiftKey){
21802 newDate = this.moveMonth(this.date, dir);
21803 newViewDate = this.moveMonth(this.viewDate, dir);
21805 newDate = new Date(this.date);
21806 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21807 newViewDate = new Date(this.viewDate);
21808 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21810 if (this.dateWithinRange(newDate)){
21811 this.date = newDate;
21812 this.viewDate = newViewDate;
21813 this.setValue(this.formatDate(this.date));
21815 e.preventDefault();
21816 dateChanged = true;
21820 this.setValue(this.formatDate(this.date));
21822 e.preventDefault();
21825 this.setValue(this.formatDate(this.date));
21839 onClick: function(e)
21841 e.stopPropagation();
21842 e.preventDefault();
21844 var target = e.getTarget();
21846 if(target.nodeName.toLowerCase() === 'i'){
21847 target = Roo.get(target).dom.parentNode;
21850 var nodeName = target.nodeName;
21851 var className = target.className;
21852 var html = target.innerHTML;
21853 //Roo.log(nodeName);
21855 switch(nodeName.toLowerCase()) {
21857 switch(className) {
21863 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21864 switch(this.viewMode){
21866 this.viewDate = this.moveMonth(this.viewDate, dir);
21870 this.viewDate = this.moveYear(this.viewDate, dir);
21876 var date = new Date();
21877 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21879 this.setValue(this.formatDate(this.date));
21886 if (className.indexOf('disabled') < 0) {
21887 this.viewDate.setUTCDate(1);
21888 if (className.indexOf('month') > -1) {
21889 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21891 var year = parseInt(html, 10) || 0;
21892 this.viewDate.setUTCFullYear(year);
21896 if(this.singleMode){
21897 this.setValue(this.formatDate(this.viewDate));
21908 //Roo.log(className);
21909 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21910 var day = parseInt(html, 10) || 1;
21911 var year = (this.viewDate || new Date()).getUTCFullYear(),
21912 month = (this.viewDate || new Date()).getUTCMonth();
21914 if (className.indexOf('old') > -1) {
21921 } else if (className.indexOf('new') > -1) {
21929 //Roo.log([year,month,day]);
21930 this.date = this.UTCDate(year, month, day,0,0,0,0);
21931 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21933 //Roo.log(this.formatDate(this.date));
21934 this.setValue(this.formatDate(this.date));
21941 setStartDate: function(startDate)
21943 this.startDate = startDate || -Infinity;
21944 if (this.startDate !== -Infinity) {
21945 this.startDate = this.parseDate(this.startDate);
21948 this.updateNavArrows();
21951 setEndDate: function(endDate)
21953 this.endDate = endDate || Infinity;
21954 if (this.endDate !== Infinity) {
21955 this.endDate = this.parseDate(this.endDate);
21958 this.updateNavArrows();
21961 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21963 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21964 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21965 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21967 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21968 return parseInt(d, 10);
21971 this.updateNavArrows();
21974 updateNavArrows: function()
21976 if(this.singleMode){
21980 var d = new Date(this.viewDate),
21981 year = d.getUTCFullYear(),
21982 month = d.getUTCMonth();
21984 Roo.each(this.picker().select('.prev', true).elements, function(v){
21986 switch (this.viewMode) {
21989 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21995 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22002 Roo.each(this.picker().select('.next', true).elements, function(v){
22004 switch (this.viewMode) {
22007 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22013 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22021 moveMonth: function(date, dir)
22026 var new_date = new Date(date.valueOf()),
22027 day = new_date.getUTCDate(),
22028 month = new_date.getUTCMonth(),
22029 mag = Math.abs(dir),
22031 dir = dir > 0 ? 1 : -1;
22034 // If going back one month, make sure month is not current month
22035 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22037 return new_date.getUTCMonth() == month;
22039 // If going forward one month, make sure month is as expected
22040 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22042 return new_date.getUTCMonth() != new_month;
22044 new_month = month + dir;
22045 new_date.setUTCMonth(new_month);
22046 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22047 if (new_month < 0 || new_month > 11) {
22048 new_month = (new_month + 12) % 12;
22051 // For magnitudes >1, move one month at a time...
22052 for (var i=0; i<mag; i++) {
22053 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22054 new_date = this.moveMonth(new_date, dir);
22056 // ...then reset the day, keeping it in the new month
22057 new_month = new_date.getUTCMonth();
22058 new_date.setUTCDate(day);
22060 return new_month != new_date.getUTCMonth();
22063 // Common date-resetting loop -- if date is beyond end of month, make it
22066 new_date.setUTCDate(--day);
22067 new_date.setUTCMonth(new_month);
22072 moveYear: function(date, dir)
22074 return this.moveMonth(date, dir*12);
22077 dateWithinRange: function(date)
22079 return date >= this.startDate && date <= this.endDate;
22085 this.picker().remove();
22088 validateValue : function(value)
22090 if(this.getVisibilityEl().hasClass('hidden')){
22094 if(value.length < 1) {
22095 if(this.allowBlank){
22101 if(value.length < this.minLength){
22104 if(value.length > this.maxLength){
22108 var vt = Roo.form.VTypes;
22109 if(!vt[this.vtype](value, this)){
22113 if(typeof this.validator == "function"){
22114 var msg = this.validator(value);
22120 if(this.regex && !this.regex.test(value)){
22124 if(typeof(this.parseDate(value)) == 'undefined'){
22128 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22132 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22142 this.date = this.viewDate = '';
22144 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22149 Roo.apply(Roo.bootstrap.DateField, {
22160 html: '<i class="fa fa-arrow-left"/>'
22170 html: '<i class="fa fa-arrow-right"/>'
22212 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22213 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22214 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22215 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22216 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22229 navFnc: 'FullYear',
22234 navFnc: 'FullYear',
22239 Roo.apply(Roo.bootstrap.DateField, {
22243 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22247 cls: 'datepicker-days',
22251 cls: 'table-condensed',
22253 Roo.bootstrap.DateField.head,
22257 Roo.bootstrap.DateField.footer
22264 cls: 'datepicker-months',
22268 cls: 'table-condensed',
22270 Roo.bootstrap.DateField.head,
22271 Roo.bootstrap.DateField.content,
22272 Roo.bootstrap.DateField.footer
22279 cls: 'datepicker-years',
22283 cls: 'table-condensed',
22285 Roo.bootstrap.DateField.head,
22286 Roo.bootstrap.DateField.content,
22287 Roo.bootstrap.DateField.footer
22306 * @class Roo.bootstrap.TimeField
22307 * @extends Roo.bootstrap.Input
22308 * Bootstrap DateField class
22312 * Create a new TimeField
22313 * @param {Object} config The config object
22316 Roo.bootstrap.TimeField = function(config){
22317 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22321 * Fires when this field show.
22322 * @param {Roo.bootstrap.DateField} thisthis
22323 * @param {Mixed} date The date value
22328 * Fires when this field hide.
22329 * @param {Roo.bootstrap.DateField} this
22330 * @param {Mixed} date The date value
22335 * Fires when select a date.
22336 * @param {Roo.bootstrap.DateField} this
22337 * @param {Mixed} date The date value
22343 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22346 * @cfg {String} format
22347 * The default time format string which can be overriden for localization support. The format must be
22348 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22352 getAutoCreate : function()
22354 this.after = '<i class="fa far fa-clock"></i>';
22355 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22359 onRender: function(ct, position)
22362 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22364 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22366 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22368 this.pop = this.picker().select('>.datepicker-time',true).first();
22369 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22371 this.picker().on('mousedown', this.onMousedown, this);
22372 this.picker().on('click', this.onClick, this);
22374 this.picker().addClass('datepicker-dropdown');
22379 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22380 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22381 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22382 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22383 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22384 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22388 fireKey: function(e){
22389 if (!this.picker().isVisible()){
22390 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22396 e.preventDefault();
22404 this.onTogglePeriod();
22407 this.onIncrementMinutes();
22410 this.onDecrementMinutes();
22419 onClick: function(e) {
22420 e.stopPropagation();
22421 e.preventDefault();
22424 picker : function()
22426 return this.pickerEl;
22429 fillTime: function()
22431 var time = this.pop.select('tbody', true).first();
22433 time.dom.innerHTML = '';
22448 cls: 'hours-up fa fas fa-chevron-up'
22468 cls: 'minutes-up fa fas fa-chevron-up'
22489 cls: 'timepicker-hour',
22504 cls: 'timepicker-minute',
22519 cls: 'btn btn-primary period',
22541 cls: 'hours-down fa fas fa-chevron-down'
22561 cls: 'minutes-down fa fas fa-chevron-down'
22579 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22586 var hours = this.time.getHours();
22587 var minutes = this.time.getMinutes();
22600 hours = hours - 12;
22604 hours = '0' + hours;
22608 minutes = '0' + minutes;
22611 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22612 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22613 this.pop.select('button', true).first().dom.innerHTML = period;
22619 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22621 var cls = ['bottom'];
22623 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22630 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22634 //this.picker().setXY(20000,20000);
22635 this.picker().addClass(cls.join('-'));
22639 Roo.each(cls, function(c){
22644 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22645 //_this.picker().setTop(_this.inputEl().getHeight());
22649 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22651 //_this.picker().setTop(0 - _this.picker().getHeight());
22656 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22660 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22668 onFocus : function()
22670 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22674 onBlur : function()
22676 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22682 this.picker().show();
22687 this.fireEvent('show', this, this.date);
22692 this.picker().hide();
22695 this.fireEvent('hide', this, this.date);
22698 setTime : function()
22701 this.setValue(this.time.format(this.format));
22703 this.fireEvent('select', this, this.date);
22708 onMousedown: function(e){
22709 e.stopPropagation();
22710 e.preventDefault();
22713 onIncrementHours: function()
22715 Roo.log('onIncrementHours');
22716 this.time = this.time.add(Date.HOUR, 1);
22721 onDecrementHours: function()
22723 Roo.log('onDecrementHours');
22724 this.time = this.time.add(Date.HOUR, -1);
22728 onIncrementMinutes: function()
22730 Roo.log('onIncrementMinutes');
22731 this.time = this.time.add(Date.MINUTE, 1);
22735 onDecrementMinutes: function()
22737 Roo.log('onDecrementMinutes');
22738 this.time = this.time.add(Date.MINUTE, -1);
22742 onTogglePeriod: function()
22744 Roo.log('onTogglePeriod');
22745 this.time = this.time.add(Date.HOUR, 12);
22753 Roo.apply(Roo.bootstrap.TimeField, {
22757 cls: 'datepicker dropdown-menu',
22761 cls: 'datepicker-time',
22765 cls: 'table-condensed',
22794 cls: 'btn btn-info ok',
22822 * @class Roo.bootstrap.MonthField
22823 * @extends Roo.bootstrap.Input
22824 * Bootstrap MonthField class
22826 * @cfg {String} language default en
22829 * Create a new MonthField
22830 * @param {Object} config The config object
22833 Roo.bootstrap.MonthField = function(config){
22834 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22839 * Fires when this field show.
22840 * @param {Roo.bootstrap.MonthField} this
22841 * @param {Mixed} date The date value
22846 * Fires when this field hide.
22847 * @param {Roo.bootstrap.MonthField} this
22848 * @param {Mixed} date The date value
22853 * Fires when select a date.
22854 * @param {Roo.bootstrap.MonthField} this
22855 * @param {String} oldvalue The old value
22856 * @param {String} newvalue The new value
22862 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22864 onRender: function(ct, position)
22867 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22869 this.language = this.language || 'en';
22870 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22871 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22873 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22874 this.isInline = false;
22875 this.isInput = true;
22876 this.component = this.el.select('.add-on', true).first() || false;
22877 this.component = (this.component && this.component.length === 0) ? false : this.component;
22878 this.hasInput = this.component && this.inputEL().length;
22880 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22882 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22884 this.picker().on('mousedown', this.onMousedown, this);
22885 this.picker().on('click', this.onClick, this);
22887 this.picker().addClass('datepicker-dropdown');
22889 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22890 v.setStyle('width', '189px');
22897 if(this.isInline) {
22903 setValue: function(v, suppressEvent)
22905 var o = this.getValue();
22907 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22911 if(suppressEvent !== true){
22912 this.fireEvent('select', this, o, v);
22917 getValue: function()
22922 onClick: function(e)
22924 e.stopPropagation();
22925 e.preventDefault();
22927 var target = e.getTarget();
22929 if(target.nodeName.toLowerCase() === 'i'){
22930 target = Roo.get(target).dom.parentNode;
22933 var nodeName = target.nodeName;
22934 var className = target.className;
22935 var html = target.innerHTML;
22937 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22941 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22943 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22949 picker : function()
22951 return this.pickerEl;
22954 fillMonths: function()
22957 var months = this.picker().select('>.datepicker-months td', true).first();
22959 months.dom.innerHTML = '';
22965 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22968 months.createChild(month);
22977 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22978 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22981 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22982 e.removeClass('active');
22984 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22985 e.addClass('active');
22992 if(this.isInline) {
22996 this.picker().removeClass(['bottom', 'top']);
22998 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23000 * place to the top of element!
23004 this.picker().addClass('top');
23005 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23010 this.picker().addClass('bottom');
23012 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23015 onFocus : function()
23017 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23021 onBlur : function()
23023 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23025 var d = this.inputEl().getValue();
23034 this.picker().show();
23035 this.picker().select('>.datepicker-months', true).first().show();
23039 this.fireEvent('show', this, this.date);
23044 if(this.isInline) {
23047 this.picker().hide();
23048 this.fireEvent('hide', this, this.date);
23052 onMousedown: function(e)
23054 e.stopPropagation();
23055 e.preventDefault();
23060 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23064 fireKey: function(e)
23066 if (!this.picker().isVisible()){
23067 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23078 e.preventDefault();
23082 dir = e.keyCode == 37 ? -1 : 1;
23084 this.vIndex = this.vIndex + dir;
23086 if(this.vIndex < 0){
23090 if(this.vIndex > 11){
23094 if(isNaN(this.vIndex)){
23098 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23104 dir = e.keyCode == 38 ? -1 : 1;
23106 this.vIndex = this.vIndex + dir * 4;
23108 if(this.vIndex < 0){
23112 if(this.vIndex > 11){
23116 if(isNaN(this.vIndex)){
23120 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23125 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23126 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23130 e.preventDefault();
23133 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23134 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23150 this.picker().remove();
23155 Roo.apply(Roo.bootstrap.MonthField, {
23174 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23175 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23180 Roo.apply(Roo.bootstrap.MonthField, {
23184 cls: 'datepicker dropdown-menu roo-dynamic',
23188 cls: 'datepicker-months',
23192 cls: 'table-condensed',
23194 Roo.bootstrap.DateField.content
23214 * @class Roo.bootstrap.CheckBox
23215 * @extends Roo.bootstrap.Input
23216 * Bootstrap CheckBox class
23218 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23219 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23220 * @cfg {String} boxLabel The text that appears beside the checkbox
23221 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23222 * @cfg {Boolean} checked initnal the element
23223 * @cfg {Boolean} inline inline the element (default false)
23224 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23225 * @cfg {String} tooltip label tooltip
23228 * Create a new CheckBox
23229 * @param {Object} config The config object
23232 Roo.bootstrap.CheckBox = function(config){
23233 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23238 * Fires when the element is checked or unchecked.
23239 * @param {Roo.bootstrap.CheckBox} this This input
23240 * @param {Boolean} checked The new checked value
23245 * Fires when the element is click.
23246 * @param {Roo.bootstrap.CheckBox} this This input
23253 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23255 inputType: 'checkbox',
23264 // checkbox success does not make any sense really..
23269 getAutoCreate : function()
23271 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23277 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23280 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23286 type : this.inputType,
23287 value : this.inputValue,
23288 cls : 'roo-' + this.inputType, //'form-box',
23289 placeholder : this.placeholder || ''
23293 if(this.inputType != 'radio'){
23297 cls : 'roo-hidden-value',
23298 value : this.checked ? this.inputValue : this.valueOff
23303 if (this.weight) { // Validity check?
23304 cfg.cls += " " + this.inputType + "-" + this.weight;
23307 if (this.disabled) {
23308 input.disabled=true;
23312 input.checked = this.checked;
23317 input.name = this.name;
23319 if(this.inputType != 'radio'){
23320 hidden.name = this.name;
23321 input.name = '_hidden_' + this.name;
23326 input.cls += ' input-' + this.size;
23331 ['xs','sm','md','lg'].map(function(size){
23332 if (settings[size]) {
23333 cfg.cls += ' col-' + size + '-' + settings[size];
23337 var inputblock = input;
23339 if (this.before || this.after) {
23342 cls : 'input-group',
23347 inputblock.cn.push({
23349 cls : 'input-group-addon',
23354 inputblock.cn.push(input);
23356 if(this.inputType != 'radio'){
23357 inputblock.cn.push(hidden);
23361 inputblock.cn.push({
23363 cls : 'input-group-addon',
23369 var boxLabelCfg = false;
23375 //'for': id, // box label is handled by onclick - so no for...
23377 html: this.boxLabel
23380 boxLabelCfg.tooltip = this.tooltip;
23386 if (align ==='left' && this.fieldLabel.length) {
23387 // Roo.log("left and has label");
23392 cls : 'control-label',
23393 html : this.fieldLabel
23404 cfg.cn[1].cn.push(boxLabelCfg);
23407 if(this.labelWidth > 12){
23408 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23411 if(this.labelWidth < 13 && this.labelmd == 0){
23412 this.labelmd = this.labelWidth;
23415 if(this.labellg > 0){
23416 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23417 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23420 if(this.labelmd > 0){
23421 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23422 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23425 if(this.labelsm > 0){
23426 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23427 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23430 if(this.labelxs > 0){
23431 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23432 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23435 } else if ( this.fieldLabel.length) {
23436 // Roo.log(" label");
23440 tag: this.boxLabel ? 'span' : 'label',
23442 cls: 'control-label box-input-label',
23443 //cls : 'input-group-addon',
23444 html : this.fieldLabel
23451 cfg.cn.push(boxLabelCfg);
23456 // Roo.log(" no label && no align");
23457 cfg.cn = [ inputblock ] ;
23459 cfg.cn.push(boxLabelCfg);
23467 if(this.inputType != 'radio'){
23468 cfg.cn.push(hidden);
23476 * return the real input element.
23478 inputEl: function ()
23480 return this.el.select('input.roo-' + this.inputType,true).first();
23482 hiddenEl: function ()
23484 return this.el.select('input.roo-hidden-value',true).first();
23487 labelEl: function()
23489 return this.el.select('label.control-label',true).first();
23491 /* depricated... */
23495 return this.labelEl();
23498 boxLabelEl: function()
23500 return this.el.select('label.box-label',true).first();
23503 initEvents : function()
23505 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23507 this.inputEl().on('click', this.onClick, this);
23509 if (this.boxLabel) {
23510 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23513 this.startValue = this.getValue();
23516 Roo.bootstrap.CheckBox.register(this);
23520 onClick : function(e)
23522 if(this.fireEvent('click', this, e) !== false){
23523 this.setChecked(!this.checked);
23528 setChecked : function(state,suppressEvent)
23530 this.startValue = this.getValue();
23532 if(this.inputType == 'radio'){
23534 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23535 e.dom.checked = false;
23538 this.inputEl().dom.checked = true;
23540 this.inputEl().dom.value = this.inputValue;
23542 if(suppressEvent !== true){
23543 this.fireEvent('check', this, true);
23551 this.checked = state;
23553 this.inputEl().dom.checked = state;
23556 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23558 if(suppressEvent !== true){
23559 this.fireEvent('check', this, state);
23565 getValue : function()
23567 if(this.inputType == 'radio'){
23568 return this.getGroupValue();
23571 return this.hiddenEl().dom.value;
23575 getGroupValue : function()
23577 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23581 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23584 setValue : function(v,suppressEvent)
23586 if(this.inputType == 'radio'){
23587 this.setGroupValue(v, suppressEvent);
23591 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23596 setGroupValue : function(v, suppressEvent)
23598 this.startValue = this.getValue();
23600 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23601 e.dom.checked = false;
23603 if(e.dom.value == v){
23604 e.dom.checked = true;
23608 if(suppressEvent !== true){
23609 this.fireEvent('check', this, true);
23617 validate : function()
23619 if(this.getVisibilityEl().hasClass('hidden')){
23625 (this.inputType == 'radio' && this.validateRadio()) ||
23626 (this.inputType == 'checkbox' && this.validateCheckbox())
23632 this.markInvalid();
23636 validateRadio : function()
23638 if(this.getVisibilityEl().hasClass('hidden')){
23642 if(this.allowBlank){
23648 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23649 if(!e.dom.checked){
23661 validateCheckbox : function()
23664 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23665 //return (this.getValue() == this.inputValue) ? true : false;
23668 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23676 for(var i in group){
23677 if(group[i].el.isVisible(true)){
23685 for(var i in group){
23690 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23697 * Mark this field as valid
23699 markValid : function()
23703 this.fireEvent('valid', this);
23705 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23708 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23715 if(this.inputType == 'radio'){
23716 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23717 var fg = e.findParent('.form-group', false, true);
23718 if (Roo.bootstrap.version == 3) {
23719 fg.removeClass([_this.invalidClass, _this.validClass]);
23720 fg.addClass(_this.validClass);
23722 fg.removeClass(['is-valid', 'is-invalid']);
23723 fg.addClass('is-valid');
23731 var fg = this.el.findParent('.form-group', false, true);
23732 if (Roo.bootstrap.version == 3) {
23733 fg.removeClass([this.invalidClass, this.validClass]);
23734 fg.addClass(this.validClass);
23736 fg.removeClass(['is-valid', 'is-invalid']);
23737 fg.addClass('is-valid');
23742 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23748 for(var i in group){
23749 var fg = group[i].el.findParent('.form-group', false, true);
23750 if (Roo.bootstrap.version == 3) {
23751 fg.removeClass([this.invalidClass, this.validClass]);
23752 fg.addClass(this.validClass);
23754 fg.removeClass(['is-valid', 'is-invalid']);
23755 fg.addClass('is-valid');
23761 * Mark this field as invalid
23762 * @param {String} msg The validation message
23764 markInvalid : function(msg)
23766 if(this.allowBlank){
23772 this.fireEvent('invalid', this, msg);
23774 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23777 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23781 label.markInvalid();
23784 if(this.inputType == 'radio'){
23786 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23787 var fg = e.findParent('.form-group', false, true);
23788 if (Roo.bootstrap.version == 3) {
23789 fg.removeClass([_this.invalidClass, _this.validClass]);
23790 fg.addClass(_this.invalidClass);
23792 fg.removeClass(['is-invalid', 'is-valid']);
23793 fg.addClass('is-invalid');
23801 var fg = this.el.findParent('.form-group', false, true);
23802 if (Roo.bootstrap.version == 3) {
23803 fg.removeClass([_this.invalidClass, _this.validClass]);
23804 fg.addClass(_this.invalidClass);
23806 fg.removeClass(['is-invalid', 'is-valid']);
23807 fg.addClass('is-invalid');
23812 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23818 for(var i in group){
23819 var fg = group[i].el.findParent('.form-group', false, true);
23820 if (Roo.bootstrap.version == 3) {
23821 fg.removeClass([_this.invalidClass, _this.validClass]);
23822 fg.addClass(_this.invalidClass);
23824 fg.removeClass(['is-invalid', 'is-valid']);
23825 fg.addClass('is-invalid');
23831 clearInvalid : function()
23833 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23835 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23837 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23839 if (label && label.iconEl) {
23840 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23841 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23845 disable : function()
23847 if(this.inputType != 'radio'){
23848 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23855 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23856 _this.getActionEl().addClass(this.disabledClass);
23857 e.dom.disabled = true;
23861 this.disabled = true;
23862 this.fireEvent("disable", this);
23866 enable : function()
23868 if(this.inputType != 'radio'){
23869 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23876 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23877 _this.getActionEl().removeClass(this.disabledClass);
23878 e.dom.disabled = false;
23882 this.disabled = false;
23883 this.fireEvent("enable", this);
23887 setBoxLabel : function(v)
23892 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23898 Roo.apply(Roo.bootstrap.CheckBox, {
23903 * register a CheckBox Group
23904 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23906 register : function(checkbox)
23908 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23909 this.groups[checkbox.groupId] = {};
23912 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23916 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23920 * fetch a CheckBox Group based on the group ID
23921 * @param {string} the group ID
23922 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23924 get: function(groupId) {
23925 if (typeof(this.groups[groupId]) == 'undefined') {
23929 return this.groups[groupId] ;
23942 * @class Roo.bootstrap.Radio
23943 * @extends Roo.bootstrap.Component
23944 * Bootstrap Radio class
23945 * @cfg {String} boxLabel - the label associated
23946 * @cfg {String} value - the value of radio
23949 * Create a new Radio
23950 * @param {Object} config The config object
23952 Roo.bootstrap.Radio = function(config){
23953 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23957 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23963 getAutoCreate : function()
23967 cls : 'form-group radio',
23972 html : this.boxLabel
23980 initEvents : function()
23982 this.parent().register(this);
23984 this.el.on('click', this.onClick, this);
23988 onClick : function(e)
23990 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23991 this.setChecked(true);
23995 setChecked : function(state, suppressEvent)
23997 this.parent().setValue(this.value, suppressEvent);
24001 setBoxLabel : function(v)
24006 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24021 * @class Roo.bootstrap.SecurePass
24022 * @extends Roo.bootstrap.Input
24023 * Bootstrap SecurePass class
24027 * Create a new SecurePass
24028 * @param {Object} config The config object
24031 Roo.bootstrap.SecurePass = function (config) {
24032 // these go here, so the translation tool can replace them..
24034 PwdEmpty: "Please type a password, and then retype it to confirm.",
24035 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24036 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24037 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24038 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24039 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24040 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24041 TooWeak: "Your password is Too Weak."
24043 this.meterLabel = "Password strength:";
24044 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24045 this.meterClass = [
24046 "roo-password-meter-tooweak",
24047 "roo-password-meter-weak",
24048 "roo-password-meter-medium",
24049 "roo-password-meter-strong",
24050 "roo-password-meter-grey"
24055 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24058 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24060 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24062 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24063 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24064 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24065 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24066 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24067 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24068 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24078 * @cfg {String/Object} Label for the strength meter (defaults to
24079 * 'Password strength:')
24084 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24085 * ['Weak', 'Medium', 'Strong'])
24088 pwdStrengths: false,
24101 initEvents: function ()
24103 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24105 if (this.el.is('input[type=password]') && Roo.isSafari) {
24106 this.el.on('keydown', this.SafariOnKeyDown, this);
24109 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24112 onRender: function (ct, position)
24114 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24115 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24116 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24118 this.trigger.createChild({
24123 cls: 'roo-password-meter-grey col-xs-12',
24126 //width: this.meterWidth + 'px'
24130 cls: 'roo-password-meter-text'
24136 if (this.hideTrigger) {
24137 this.trigger.setDisplayed(false);
24139 this.setSize(this.width || '', this.height || '');
24142 onDestroy: function ()
24144 if (this.trigger) {
24145 this.trigger.removeAllListeners();
24146 this.trigger.remove();
24149 this.wrap.remove();
24151 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24154 checkStrength: function ()
24156 var pwd = this.inputEl().getValue();
24157 if (pwd == this._lastPwd) {
24162 if (this.ClientSideStrongPassword(pwd)) {
24164 } else if (this.ClientSideMediumPassword(pwd)) {
24166 } else if (this.ClientSideWeakPassword(pwd)) {
24172 Roo.log('strength1: ' + strength);
24174 //var pm = this.trigger.child('div/div/div').dom;
24175 var pm = this.trigger.child('div/div');
24176 pm.removeClass(this.meterClass);
24177 pm.addClass(this.meterClass[strength]);
24180 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24182 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24184 this._lastPwd = pwd;
24188 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24190 this._lastPwd = '';
24192 var pm = this.trigger.child('div/div');
24193 pm.removeClass(this.meterClass);
24194 pm.addClass('roo-password-meter-grey');
24197 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24200 this.inputEl().dom.type='password';
24203 validateValue: function (value)
24205 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24208 if (value.length == 0) {
24209 if (this.allowBlank) {
24210 this.clearInvalid();
24214 this.markInvalid(this.errors.PwdEmpty);
24215 this.errorMsg = this.errors.PwdEmpty;
24223 if (!value.match(/[\x21-\x7e]+/)) {
24224 this.markInvalid(this.errors.PwdBadChar);
24225 this.errorMsg = this.errors.PwdBadChar;
24228 if (value.length < 6) {
24229 this.markInvalid(this.errors.PwdShort);
24230 this.errorMsg = this.errors.PwdShort;
24233 if (value.length > 16) {
24234 this.markInvalid(this.errors.PwdLong);
24235 this.errorMsg = this.errors.PwdLong;
24239 if (this.ClientSideStrongPassword(value)) {
24241 } else if (this.ClientSideMediumPassword(value)) {
24243 } else if (this.ClientSideWeakPassword(value)) {
24250 if (strength < 2) {
24251 //this.markInvalid(this.errors.TooWeak);
24252 this.errorMsg = this.errors.TooWeak;
24257 console.log('strength2: ' + strength);
24259 //var pm = this.trigger.child('div/div/div').dom;
24261 var pm = this.trigger.child('div/div');
24262 pm.removeClass(this.meterClass);
24263 pm.addClass(this.meterClass[strength]);
24265 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24267 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24269 this.errorMsg = '';
24273 CharacterSetChecks: function (type)
24276 this.fResult = false;
24279 isctype: function (character, type)
24282 case this.kCapitalLetter:
24283 if (character >= 'A' && character <= 'Z') {
24288 case this.kSmallLetter:
24289 if (character >= 'a' && character <= 'z') {
24295 if (character >= '0' && character <= '9') {
24300 case this.kPunctuation:
24301 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24312 IsLongEnough: function (pwd, size)
24314 return !(pwd == null || isNaN(size) || pwd.length < size);
24317 SpansEnoughCharacterSets: function (word, nb)
24319 if (!this.IsLongEnough(word, nb))
24324 var characterSetChecks = new Array(
24325 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24326 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24329 for (var index = 0; index < word.length; ++index) {
24330 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24331 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24332 characterSetChecks[nCharSet].fResult = true;
24339 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24340 if (characterSetChecks[nCharSet].fResult) {
24345 if (nCharSets < nb) {
24351 ClientSideStrongPassword: function (pwd)
24353 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24356 ClientSideMediumPassword: function (pwd)
24358 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24361 ClientSideWeakPassword: function (pwd)
24363 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24366 })//<script type="text/javascript">
24369 * Based Ext JS Library 1.1.1
24370 * Copyright(c) 2006-2007, Ext JS, LLC.
24376 * @class Roo.HtmlEditorCore
24377 * @extends Roo.Component
24378 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24380 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24383 Roo.HtmlEditorCore = function(config){
24386 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24391 * @event initialize
24392 * Fires when the editor is fully initialized (including the iframe)
24393 * @param {Roo.HtmlEditorCore} this
24398 * Fires when the editor is first receives the focus. Any insertion must wait
24399 * until after this event.
24400 * @param {Roo.HtmlEditorCore} this
24404 * @event beforesync
24405 * Fires before the textarea is updated with content from the editor iframe. Return false
24406 * to cancel the sync.
24407 * @param {Roo.HtmlEditorCore} this
24408 * @param {String} html
24412 * @event beforepush
24413 * Fires before the iframe editor is updated with content from the textarea. Return false
24414 * to cancel the push.
24415 * @param {Roo.HtmlEditorCore} this
24416 * @param {String} html
24421 * Fires when the textarea is updated with content from the editor iframe.
24422 * @param {Roo.HtmlEditorCore} this
24423 * @param {String} html
24428 * Fires when the iframe editor is updated with content from the textarea.
24429 * @param {Roo.HtmlEditorCore} this
24430 * @param {String} html
24435 * @event editorevent
24436 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24437 * @param {Roo.HtmlEditorCore} this
24443 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24445 // defaults : white / black...
24446 this.applyBlacklists();
24453 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24457 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24463 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24468 * @cfg {Number} height (in pixels)
24472 * @cfg {Number} width (in pixels)
24477 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24480 stylesheets: false,
24485 // private properties
24486 validationEvent : false,
24488 initialized : false,
24490 sourceEditMode : false,
24491 onFocus : Roo.emptyFn,
24493 hideMode:'offsets',
24497 // blacklist + whitelisted elements..
24504 * Protected method that will not generally be called directly. It
24505 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24506 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24508 getDocMarkup : function(){
24512 // inherit styels from page...??
24513 if (this.stylesheets === false) {
24515 Roo.get(document.head).select('style').each(function(node) {
24516 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24519 Roo.get(document.head).select('link').each(function(node) {
24520 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24523 } else if (!this.stylesheets.length) {
24525 st = '<style type="text/css">' +
24526 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24529 for (var i in this.stylesheets) {
24530 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24535 st += '<style type="text/css">' +
24536 'IMG { cursor: pointer } ' +
24539 var cls = 'roo-htmleditor-body';
24541 if(this.bodyCls.length){
24542 cls += ' ' + this.bodyCls;
24545 return '<html><head>' + st +
24546 //<style type="text/css">' +
24547 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24549 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24553 onRender : function(ct, position)
24556 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24557 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24560 this.el.dom.style.border = '0 none';
24561 this.el.dom.setAttribute('tabIndex', -1);
24562 this.el.addClass('x-hidden hide');
24566 if(Roo.isIE){ // fix IE 1px bogus margin
24567 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24571 this.frameId = Roo.id();
24575 var iframe = this.owner.wrap.createChild({
24577 cls: 'form-control', // bootstrap..
24579 name: this.frameId,
24580 frameBorder : 'no',
24581 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24586 this.iframe = iframe.dom;
24588 this.assignDocWin();
24590 this.doc.designMode = 'on';
24593 this.doc.write(this.getDocMarkup());
24597 var task = { // must defer to wait for browser to be ready
24599 //console.log("run task?" + this.doc.readyState);
24600 this.assignDocWin();
24601 if(this.doc.body || this.doc.readyState == 'complete'){
24603 this.doc.designMode="on";
24607 Roo.TaskMgr.stop(task);
24608 this.initEditor.defer(10, this);
24615 Roo.TaskMgr.start(task);
24620 onResize : function(w, h)
24622 Roo.log('resize: ' +w + ',' + h );
24623 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24627 if(typeof w == 'number'){
24629 this.iframe.style.width = w + 'px';
24631 if(typeof h == 'number'){
24633 this.iframe.style.height = h + 'px';
24635 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24642 * Toggles the editor between standard and source edit mode.
24643 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24645 toggleSourceEdit : function(sourceEditMode){
24647 this.sourceEditMode = sourceEditMode === true;
24649 if(this.sourceEditMode){
24651 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24654 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24655 //this.iframe.className = '';
24658 //this.setSize(this.owner.wrap.getSize());
24659 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24666 * Protected method that will not generally be called directly. If you need/want
24667 * custom HTML cleanup, this is the method you should override.
24668 * @param {String} html The HTML to be cleaned
24669 * return {String} The cleaned HTML
24671 cleanHtml : function(html){
24672 html = String(html);
24673 if(html.length > 5){
24674 if(Roo.isSafari){ // strip safari nonsense
24675 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24678 if(html == ' '){
24685 * HTML Editor -> Textarea
24686 * Protected method that will not generally be called directly. Syncs the contents
24687 * of the editor iframe with the textarea.
24689 syncValue : function(){
24690 if(this.initialized){
24691 var bd = (this.doc.body || this.doc.documentElement);
24692 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24693 var html = bd.innerHTML;
24695 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24696 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24698 html = '<div style="'+m[0]+'">' + html + '</div>';
24701 html = this.cleanHtml(html);
24702 // fix up the special chars.. normaly like back quotes in word...
24703 // however we do not want to do this with chinese..
24704 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24706 var cc = match.charCodeAt();
24708 // Get the character value, handling surrogate pairs
24709 if (match.length == 2) {
24710 // It's a surrogate pair, calculate the Unicode code point
24711 var high = match.charCodeAt(0) - 0xD800;
24712 var low = match.charCodeAt(1) - 0xDC00;
24713 cc = (high * 0x400) + low + 0x10000;
24715 (cc >= 0x4E00 && cc < 0xA000 ) ||
24716 (cc >= 0x3400 && cc < 0x4E00 ) ||
24717 (cc >= 0xf900 && cc < 0xfb00 )
24722 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24723 return "&#" + cc + ";";
24730 if(this.owner.fireEvent('beforesync', this, html) !== false){
24731 this.el.dom.value = html;
24732 this.owner.fireEvent('sync', this, html);
24738 * Protected method that will not generally be called directly. Pushes the value of the textarea
24739 * into the iframe editor.
24741 pushValue : function(){
24742 if(this.initialized){
24743 var v = this.el.dom.value.trim();
24745 // if(v.length < 1){
24749 if(this.owner.fireEvent('beforepush', this, v) !== false){
24750 var d = (this.doc.body || this.doc.documentElement);
24752 this.cleanUpPaste();
24753 this.el.dom.value = d.innerHTML;
24754 this.owner.fireEvent('push', this, v);
24760 deferFocus : function(){
24761 this.focus.defer(10, this);
24765 focus : function(){
24766 if(this.win && !this.sourceEditMode){
24773 assignDocWin: function()
24775 var iframe = this.iframe;
24778 this.doc = iframe.contentWindow.document;
24779 this.win = iframe.contentWindow;
24781 // if (!Roo.get(this.frameId)) {
24784 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24785 // this.win = Roo.get(this.frameId).dom.contentWindow;
24787 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24791 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24792 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24797 initEditor : function(){
24798 //console.log("INIT EDITOR");
24799 this.assignDocWin();
24803 this.doc.designMode="on";
24805 this.doc.write(this.getDocMarkup());
24808 var dbody = (this.doc.body || this.doc.documentElement);
24809 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24810 // this copies styles from the containing element into thsi one..
24811 // not sure why we need all of this..
24812 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24814 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24815 //ss['background-attachment'] = 'fixed'; // w3c
24816 dbody.bgProperties = 'fixed'; // ie
24817 //Roo.DomHelper.applyStyles(dbody, ss);
24818 Roo.EventManager.on(this.doc, {
24819 //'mousedown': this.onEditorEvent,
24820 'mouseup': this.onEditorEvent,
24821 'dblclick': this.onEditorEvent,
24822 'click': this.onEditorEvent,
24823 'keyup': this.onEditorEvent,
24828 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24830 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24831 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24833 this.initialized = true;
24835 this.owner.fireEvent('initialize', this);
24840 onDestroy : function(){
24846 //for (var i =0; i < this.toolbars.length;i++) {
24847 // // fixme - ask toolbars for heights?
24848 // this.toolbars[i].onDestroy();
24851 //this.wrap.dom.innerHTML = '';
24852 //this.wrap.remove();
24857 onFirstFocus : function(){
24859 this.assignDocWin();
24862 this.activated = true;
24865 if(Roo.isGecko){ // prevent silly gecko errors
24867 var s = this.win.getSelection();
24868 if(!s.focusNode || s.focusNode.nodeType != 3){
24869 var r = s.getRangeAt(0);
24870 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24875 this.execCmd('useCSS', true);
24876 this.execCmd('styleWithCSS', false);
24879 this.owner.fireEvent('activate', this);
24883 adjustFont: function(btn){
24884 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24885 //if(Roo.isSafari){ // safari
24888 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24889 if(Roo.isSafari){ // safari
24890 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24891 v = (v < 10) ? 10 : v;
24892 v = (v > 48) ? 48 : v;
24893 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24898 v = Math.max(1, v+adjust);
24900 this.execCmd('FontSize', v );
24903 onEditorEvent : function(e)
24905 this.owner.fireEvent('editorevent', this, e);
24906 // this.updateToolbar();
24907 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24910 insertTag : function(tg)
24912 // could be a bit smarter... -> wrap the current selected tRoo..
24913 if (tg.toLowerCase() == 'span' ||
24914 tg.toLowerCase() == 'code' ||
24915 tg.toLowerCase() == 'sup' ||
24916 tg.toLowerCase() == 'sub'
24919 range = this.createRange(this.getSelection());
24920 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24921 wrappingNode.appendChild(range.extractContents());
24922 range.insertNode(wrappingNode);
24929 this.execCmd("formatblock", tg);
24933 insertText : function(txt)
24937 var range = this.createRange();
24938 range.deleteContents();
24939 //alert(Sender.getAttribute('label'));
24941 range.insertNode(this.doc.createTextNode(txt));
24947 * Executes a Midas editor command on the editor document and performs necessary focus and
24948 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24949 * @param {String} cmd The Midas command
24950 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24952 relayCmd : function(cmd, value){
24954 this.execCmd(cmd, value);
24955 this.owner.fireEvent('editorevent', this);
24956 //this.updateToolbar();
24957 this.owner.deferFocus();
24961 * Executes a Midas editor command directly on the editor document.
24962 * For visual commands, you should use {@link #relayCmd} instead.
24963 * <b>This should only be called after the editor is initialized.</b>
24964 * @param {String} cmd The Midas command
24965 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24967 execCmd : function(cmd, value){
24968 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24975 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24977 * @param {String} text | dom node..
24979 insertAtCursor : function(text)
24982 if(!this.activated){
24988 var r = this.doc.selection.createRange();
24999 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25003 // from jquery ui (MIT licenced)
25005 var win = this.win;
25007 if (win.getSelection && win.getSelection().getRangeAt) {
25008 range = win.getSelection().getRangeAt(0);
25009 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25010 range.insertNode(node);
25011 } else if (win.document.selection && win.document.selection.createRange) {
25012 // no firefox support
25013 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25014 win.document.selection.createRange().pasteHTML(txt);
25016 // no firefox support
25017 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25018 this.execCmd('InsertHTML', txt);
25027 mozKeyPress : function(e){
25029 var c = e.getCharCode(), cmd;
25032 c = String.fromCharCode(c).toLowerCase();
25046 this.cleanUpPaste.defer(100, this);
25054 e.preventDefault();
25062 fixKeys : function(){ // load time branching for fastest keydown performance
25064 return function(e){
25065 var k = e.getKey(), r;
25068 r = this.doc.selection.createRange();
25071 r.pasteHTML('    ');
25078 r = this.doc.selection.createRange();
25080 var target = r.parentElement();
25081 if(!target || target.tagName.toLowerCase() != 'li'){
25083 r.pasteHTML('<br />');
25089 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25090 this.cleanUpPaste.defer(100, this);
25096 }else if(Roo.isOpera){
25097 return function(e){
25098 var k = e.getKey();
25102 this.execCmd('InsertHTML','    ');
25105 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25106 this.cleanUpPaste.defer(100, this);
25111 }else if(Roo.isSafari){
25112 return function(e){
25113 var k = e.getKey();
25117 this.execCmd('InsertText','\t');
25121 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25122 this.cleanUpPaste.defer(100, this);
25130 getAllAncestors: function()
25132 var p = this.getSelectedNode();
25135 a.push(p); // push blank onto stack..
25136 p = this.getParentElement();
25140 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25144 a.push(this.doc.body);
25148 lastSelNode : false,
25151 getSelection : function()
25153 this.assignDocWin();
25154 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25157 getSelectedNode: function()
25159 // this may only work on Gecko!!!
25161 // should we cache this!!!!
25166 var range = this.createRange(this.getSelection()).cloneRange();
25169 var parent = range.parentElement();
25171 var testRange = range.duplicate();
25172 testRange.moveToElementText(parent);
25173 if (testRange.inRange(range)) {
25176 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25179 parent = parent.parentElement;
25184 // is ancestor a text element.
25185 var ac = range.commonAncestorContainer;
25186 if (ac.nodeType == 3) {
25187 ac = ac.parentNode;
25190 var ar = ac.childNodes;
25193 var other_nodes = [];
25194 var has_other_nodes = false;
25195 for (var i=0;i<ar.length;i++) {
25196 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25199 // fullly contained node.
25201 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25206 // probably selected..
25207 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25208 other_nodes.push(ar[i]);
25212 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25217 has_other_nodes = true;
25219 if (!nodes.length && other_nodes.length) {
25220 nodes= other_nodes;
25222 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25228 createRange: function(sel)
25230 // this has strange effects when using with
25231 // top toolbar - not sure if it's a great idea.
25232 //this.editor.contentWindow.focus();
25233 if (typeof sel != "undefined") {
25235 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25237 return this.doc.createRange();
25240 return this.doc.createRange();
25243 getParentElement: function()
25246 this.assignDocWin();
25247 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25249 var range = this.createRange(sel);
25252 var p = range.commonAncestorContainer;
25253 while (p.nodeType == 3) { // text node
25264 * Range intersection.. the hard stuff...
25268 * [ -- selected range --- ]
25272 * if end is before start or hits it. fail.
25273 * if start is after end or hits it fail.
25275 * if either hits (but other is outside. - then it's not
25281 // @see http://www.thismuchiknow.co.uk/?p=64.
25282 rangeIntersectsNode : function(range, node)
25284 var nodeRange = node.ownerDocument.createRange();
25286 nodeRange.selectNode(node);
25288 nodeRange.selectNodeContents(node);
25291 var rangeStartRange = range.cloneRange();
25292 rangeStartRange.collapse(true);
25294 var rangeEndRange = range.cloneRange();
25295 rangeEndRange.collapse(false);
25297 var nodeStartRange = nodeRange.cloneRange();
25298 nodeStartRange.collapse(true);
25300 var nodeEndRange = nodeRange.cloneRange();
25301 nodeEndRange.collapse(false);
25303 return rangeStartRange.compareBoundaryPoints(
25304 Range.START_TO_START, nodeEndRange) == -1 &&
25305 rangeEndRange.compareBoundaryPoints(
25306 Range.START_TO_START, nodeStartRange) == 1;
25310 rangeCompareNode : function(range, node)
25312 var nodeRange = node.ownerDocument.createRange();
25314 nodeRange.selectNode(node);
25316 nodeRange.selectNodeContents(node);
25320 range.collapse(true);
25322 nodeRange.collapse(true);
25324 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25325 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25327 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25329 var nodeIsBefore = ss == 1;
25330 var nodeIsAfter = ee == -1;
25332 if (nodeIsBefore && nodeIsAfter) {
25335 if (!nodeIsBefore && nodeIsAfter) {
25336 return 1; //right trailed.
25339 if (nodeIsBefore && !nodeIsAfter) {
25340 return 2; // left trailed.
25346 // private? - in a new class?
25347 cleanUpPaste : function()
25349 // cleans up the whole document..
25350 Roo.log('cleanuppaste');
25352 this.cleanUpChildren(this.doc.body);
25353 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25354 if (clean != this.doc.body.innerHTML) {
25355 this.doc.body.innerHTML = clean;
25360 cleanWordChars : function(input) {// change the chars to hex code
25361 var he = Roo.HtmlEditorCore;
25363 var output = input;
25364 Roo.each(he.swapCodes, function(sw) {
25365 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25367 output = output.replace(swapper, sw[1]);
25374 cleanUpChildren : function (n)
25376 if (!n.childNodes.length) {
25379 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25380 this.cleanUpChild(n.childNodes[i]);
25387 cleanUpChild : function (node)
25390 //console.log(node);
25391 if (node.nodeName == "#text") {
25392 // clean up silly Windows -- stuff?
25395 if (node.nodeName == "#comment") {
25396 node.parentNode.removeChild(node);
25397 // clean up silly Windows -- stuff?
25400 var lcname = node.tagName.toLowerCase();
25401 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25402 // whitelist of tags..
25404 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25406 node.parentNode.removeChild(node);
25411 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25413 // spans with no attributes - just remove them..
25414 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25415 remove_keep_children = true;
25418 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25419 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25421 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25422 // remove_keep_children = true;
25425 if (remove_keep_children) {
25426 this.cleanUpChildren(node);
25427 // inserts everything just before this node...
25428 while (node.childNodes.length) {
25429 var cn = node.childNodes[0];
25430 node.removeChild(cn);
25431 node.parentNode.insertBefore(cn, node);
25433 node.parentNode.removeChild(node);
25437 if (!node.attributes || !node.attributes.length) {
25442 this.cleanUpChildren(node);
25446 function cleanAttr(n,v)
25449 if (v.match(/^\./) || v.match(/^\//)) {
25452 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25455 if (v.match(/^#/)) {
25458 if (v.match(/^\{/)) { // allow template editing.
25461 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25462 node.removeAttribute(n);
25466 var cwhite = this.cwhite;
25467 var cblack = this.cblack;
25469 function cleanStyle(n,v)
25471 if (v.match(/expression/)) { //XSS?? should we even bother..
25472 node.removeAttribute(n);
25476 var parts = v.split(/;/);
25479 Roo.each(parts, function(p) {
25480 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25484 var l = p.split(':').shift().replace(/\s+/g,'');
25485 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25487 if ( cwhite.length && cblack.indexOf(l) > -1) {
25488 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25489 //node.removeAttribute(n);
25493 // only allow 'c whitelisted system attributes'
25494 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25495 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25496 //node.removeAttribute(n);
25506 if (clean.length) {
25507 node.setAttribute(n, clean.join(';'));
25509 node.removeAttribute(n);
25515 for (var i = node.attributes.length-1; i > -1 ; i--) {
25516 var a = node.attributes[i];
25519 if (a.name.toLowerCase().substr(0,2)=='on') {
25520 node.removeAttribute(a.name);
25523 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25524 node.removeAttribute(a.name);
25527 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25528 cleanAttr(a.name,a.value); // fixme..
25531 if (a.name == 'style') {
25532 cleanStyle(a.name,a.value);
25535 /// clean up MS crap..
25536 // tecnically this should be a list of valid class'es..
25539 if (a.name == 'class') {
25540 if (a.value.match(/^Mso/)) {
25541 node.removeAttribute('class');
25544 if (a.value.match(/^body$/)) {
25545 node.removeAttribute('class');
25556 this.cleanUpChildren(node);
25562 * Clean up MS wordisms...
25564 cleanWord : function(node)
25567 this.cleanWord(this.doc.body);
25572 node.nodeName == 'SPAN' &&
25573 !node.hasAttributes() &&
25574 node.childNodes.length == 1 &&
25575 node.firstChild.nodeName == "#text"
25577 var textNode = node.firstChild;
25578 node.removeChild(textNode);
25579 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25580 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25582 node.parentNode.insertBefore(textNode, node);
25583 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25584 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25586 node.parentNode.removeChild(node);
25589 if (node.nodeName == "#text") {
25590 // clean up silly Windows -- stuff?
25593 if (node.nodeName == "#comment") {
25594 node.parentNode.removeChild(node);
25595 // clean up silly Windows -- stuff?
25599 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25600 node.parentNode.removeChild(node);
25603 //Roo.log(node.tagName);
25604 // remove - but keep children..
25605 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25606 //Roo.log('-- removed');
25607 while (node.childNodes.length) {
25608 var cn = node.childNodes[0];
25609 node.removeChild(cn);
25610 node.parentNode.insertBefore(cn, node);
25611 // move node to parent - and clean it..
25612 this.cleanWord(cn);
25614 node.parentNode.removeChild(node);
25615 /// no need to iterate chidlren = it's got none..
25616 //this.iterateChildren(node, this.cleanWord);
25620 if (node.className.length) {
25622 var cn = node.className.split(/\W+/);
25624 Roo.each(cn, function(cls) {
25625 if (cls.match(/Mso[a-zA-Z]+/)) {
25630 node.className = cna.length ? cna.join(' ') : '';
25632 node.removeAttribute("class");
25636 if (node.hasAttribute("lang")) {
25637 node.removeAttribute("lang");
25640 if (node.hasAttribute("style")) {
25642 var styles = node.getAttribute("style").split(";");
25644 Roo.each(styles, function(s) {
25645 if (!s.match(/:/)) {
25648 var kv = s.split(":");
25649 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25652 // what ever is left... we allow.
25655 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25656 if (!nstyle.length) {
25657 node.removeAttribute('style');
25660 this.iterateChildren(node, this.cleanWord);
25666 * iterateChildren of a Node, calling fn each time, using this as the scole..
25667 * @param {DomNode} node node to iterate children of.
25668 * @param {Function} fn method of this class to call on each item.
25670 iterateChildren : function(node, fn)
25672 if (!node.childNodes.length) {
25675 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25676 fn.call(this, node.childNodes[i])
25682 * cleanTableWidths.
25684 * Quite often pasting from word etc.. results in tables with column and widths.
25685 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25688 cleanTableWidths : function(node)
25693 this.cleanTableWidths(this.doc.body);
25698 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25701 Roo.log(node.tagName);
25702 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25703 this.iterateChildren(node, this.cleanTableWidths);
25706 if (node.hasAttribute('width')) {
25707 node.removeAttribute('width');
25711 if (node.hasAttribute("style")) {
25714 var styles = node.getAttribute("style").split(";");
25716 Roo.each(styles, function(s) {
25717 if (!s.match(/:/)) {
25720 var kv = s.split(":");
25721 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25724 // what ever is left... we allow.
25727 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25728 if (!nstyle.length) {
25729 node.removeAttribute('style');
25733 this.iterateChildren(node, this.cleanTableWidths);
25741 domToHTML : function(currentElement, depth, nopadtext) {
25743 depth = depth || 0;
25744 nopadtext = nopadtext || false;
25746 if (!currentElement) {
25747 return this.domToHTML(this.doc.body);
25750 //Roo.log(currentElement);
25752 var allText = false;
25753 var nodeName = currentElement.nodeName;
25754 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25756 if (nodeName == '#text') {
25758 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25763 if (nodeName != 'BODY') {
25766 // Prints the node tagName, such as <A>, <IMG>, etc
25769 for(i = 0; i < currentElement.attributes.length;i++) {
25771 var aname = currentElement.attributes.item(i).name;
25772 if (!currentElement.attributes.item(i).value.length) {
25775 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25778 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25787 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25790 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25795 // Traverse the tree
25797 var currentElementChild = currentElement.childNodes.item(i);
25798 var allText = true;
25799 var innerHTML = '';
25801 while (currentElementChild) {
25802 // Formatting code (indent the tree so it looks nice on the screen)
25803 var nopad = nopadtext;
25804 if (lastnode == 'SPAN') {
25808 if (currentElementChild.nodeName == '#text') {
25809 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25810 toadd = nopadtext ? toadd : toadd.trim();
25811 if (!nopad && toadd.length > 80) {
25812 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25814 innerHTML += toadd;
25817 currentElementChild = currentElement.childNodes.item(i);
25823 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25825 // Recursively traverse the tree structure of the child node
25826 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25827 lastnode = currentElementChild.nodeName;
25829 currentElementChild=currentElement.childNodes.item(i);
25835 // The remaining code is mostly for formatting the tree
25836 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25841 ret+= "</"+tagName+">";
25847 applyBlacklists : function()
25849 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25850 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25854 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25855 if (b.indexOf(tag) > -1) {
25858 this.white.push(tag);
25862 Roo.each(w, function(tag) {
25863 if (b.indexOf(tag) > -1) {
25866 if (this.white.indexOf(tag) > -1) {
25869 this.white.push(tag);
25874 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25875 if (w.indexOf(tag) > -1) {
25878 this.black.push(tag);
25882 Roo.each(b, function(tag) {
25883 if (w.indexOf(tag) > -1) {
25886 if (this.black.indexOf(tag) > -1) {
25889 this.black.push(tag);
25894 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25895 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25899 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25900 if (b.indexOf(tag) > -1) {
25903 this.cwhite.push(tag);
25907 Roo.each(w, function(tag) {
25908 if (b.indexOf(tag) > -1) {
25911 if (this.cwhite.indexOf(tag) > -1) {
25914 this.cwhite.push(tag);
25919 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25920 if (w.indexOf(tag) > -1) {
25923 this.cblack.push(tag);
25927 Roo.each(b, function(tag) {
25928 if (w.indexOf(tag) > -1) {
25931 if (this.cblack.indexOf(tag) > -1) {
25934 this.cblack.push(tag);
25939 setStylesheets : function(stylesheets)
25941 if(typeof(stylesheets) == 'string'){
25942 Roo.get(this.iframe.contentDocument.head).createChild({
25944 rel : 'stylesheet',
25953 Roo.each(stylesheets, function(s) {
25958 Roo.get(_this.iframe.contentDocument.head).createChild({
25960 rel : 'stylesheet',
25969 removeStylesheets : function()
25973 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25978 setStyle : function(style)
25980 Roo.get(this.iframe.contentDocument.head).createChild({
25989 // hide stuff that is not compatible
26003 * @event specialkey
26007 * @cfg {String} fieldClass @hide
26010 * @cfg {String} focusClass @hide
26013 * @cfg {String} autoCreate @hide
26016 * @cfg {String} inputType @hide
26019 * @cfg {String} invalidClass @hide
26022 * @cfg {String} invalidText @hide
26025 * @cfg {String} msgFx @hide
26028 * @cfg {String} validateOnBlur @hide
26032 Roo.HtmlEditorCore.white = [
26033 'area', 'br', 'img', 'input', 'hr', 'wbr',
26035 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26036 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26037 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26038 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26039 'table', 'ul', 'xmp',
26041 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26044 'dir', 'menu', 'ol', 'ul', 'dl',
26050 Roo.HtmlEditorCore.black = [
26051 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26053 'base', 'basefont', 'bgsound', 'blink', 'body',
26054 'frame', 'frameset', 'head', 'html', 'ilayer',
26055 'iframe', 'layer', 'link', 'meta', 'object',
26056 'script', 'style' ,'title', 'xml' // clean later..
26058 Roo.HtmlEditorCore.clean = [
26059 'script', 'style', 'title', 'xml'
26061 Roo.HtmlEditorCore.remove = [
26066 Roo.HtmlEditorCore.ablack = [
26070 Roo.HtmlEditorCore.aclean = [
26071 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26075 Roo.HtmlEditorCore.pwhite= [
26076 'http', 'https', 'mailto'
26079 // white listed style attributes.
26080 Roo.HtmlEditorCore.cwhite= [
26081 // 'text-align', /// default is to allow most things..
26087 // black listed style attributes.
26088 Roo.HtmlEditorCore.cblack= [
26089 // 'font-size' -- this can be set by the project
26093 Roo.HtmlEditorCore.swapCodes =[
26094 [ 8211, "–" ],
26095 [ 8212, "—" ],
26112 * @class Roo.bootstrap.HtmlEditor
26113 * @extends Roo.bootstrap.TextArea
26114 * Bootstrap HtmlEditor class
26117 * Create a new HtmlEditor
26118 * @param {Object} config The config object
26121 Roo.bootstrap.HtmlEditor = function(config){
26122 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26123 if (!this.toolbars) {
26124 this.toolbars = [];
26127 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26130 * @event initialize
26131 * Fires when the editor is fully initialized (including the iframe)
26132 * @param {HtmlEditor} this
26137 * Fires when the editor is first receives the focus. Any insertion must wait
26138 * until after this event.
26139 * @param {HtmlEditor} this
26143 * @event beforesync
26144 * Fires before the textarea is updated with content from the editor iframe. Return false
26145 * to cancel the sync.
26146 * @param {HtmlEditor} this
26147 * @param {String} html
26151 * @event beforepush
26152 * Fires before the iframe editor is updated with content from the textarea. Return false
26153 * to cancel the push.
26154 * @param {HtmlEditor} this
26155 * @param {String} html
26160 * Fires when the textarea is updated with content from the editor iframe.
26161 * @param {HtmlEditor} this
26162 * @param {String} html
26167 * Fires when the iframe editor is updated with content from the textarea.
26168 * @param {HtmlEditor} this
26169 * @param {String} html
26173 * @event editmodechange
26174 * Fires when the editor switches edit modes
26175 * @param {HtmlEditor} this
26176 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26178 editmodechange: true,
26180 * @event editorevent
26181 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26182 * @param {HtmlEditor} this
26186 * @event firstfocus
26187 * Fires when on first focus - needed by toolbars..
26188 * @param {HtmlEditor} this
26193 * Auto save the htmlEditor value as a file into Events
26194 * @param {HtmlEditor} this
26198 * @event savedpreview
26199 * preview the saved version of htmlEditor
26200 * @param {HtmlEditor} this
26207 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26211 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26216 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26221 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26226 * @cfg {Number} height (in pixels)
26230 * @cfg {Number} width (in pixels)
26235 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26238 stylesheets: false,
26243 // private properties
26244 validationEvent : false,
26246 initialized : false,
26249 onFocus : Roo.emptyFn,
26251 hideMode:'offsets',
26253 tbContainer : false,
26257 toolbarContainer :function() {
26258 return this.wrap.select('.x-html-editor-tb',true).first();
26262 * Protected method that will not generally be called directly. It
26263 * is called when the editor creates its toolbar. Override this method if you need to
26264 * add custom toolbar buttons.
26265 * @param {HtmlEditor} editor
26267 createToolbar : function(){
26268 Roo.log('renewing');
26269 Roo.log("create toolbars");
26271 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26272 this.toolbars[0].render(this.toolbarContainer());
26276 // if (!editor.toolbars || !editor.toolbars.length) {
26277 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26280 // for (var i =0 ; i < editor.toolbars.length;i++) {
26281 // editor.toolbars[i] = Roo.factory(
26282 // typeof(editor.toolbars[i]) == 'string' ?
26283 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26284 // Roo.bootstrap.HtmlEditor);
26285 // editor.toolbars[i].init(editor);
26291 onRender : function(ct, position)
26293 // Roo.log("Call onRender: " + this.xtype);
26295 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26297 this.wrap = this.inputEl().wrap({
26298 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26301 this.editorcore.onRender(ct, position);
26303 if (this.resizable) {
26304 this.resizeEl = new Roo.Resizable(this.wrap, {
26308 minHeight : this.height,
26309 height: this.height,
26310 handles : this.resizable,
26313 resize : function(r, w, h) {
26314 _t.onResize(w,h); // -something
26320 this.createToolbar(this);
26323 if(!this.width && this.resizable){
26324 this.setSize(this.wrap.getSize());
26326 if (this.resizeEl) {
26327 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26328 // should trigger onReize..
26334 onResize : function(w, h)
26336 Roo.log('resize: ' +w + ',' + h );
26337 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26341 if(this.inputEl() ){
26342 if(typeof w == 'number'){
26343 var aw = w - this.wrap.getFrameWidth('lr');
26344 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26347 if(typeof h == 'number'){
26348 var tbh = -11; // fixme it needs to tool bar size!
26349 for (var i =0; i < this.toolbars.length;i++) {
26350 // fixme - ask toolbars for heights?
26351 tbh += this.toolbars[i].el.getHeight();
26352 //if (this.toolbars[i].footer) {
26353 // tbh += this.toolbars[i].footer.el.getHeight();
26361 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26362 ah -= 5; // knock a few pixes off for look..
26363 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26367 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26368 this.editorcore.onResize(ew,eh);
26373 * Toggles the editor between standard and source edit mode.
26374 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26376 toggleSourceEdit : function(sourceEditMode)
26378 this.editorcore.toggleSourceEdit(sourceEditMode);
26380 if(this.editorcore.sourceEditMode){
26381 Roo.log('editor - showing textarea');
26384 // Roo.log(this.syncValue());
26386 this.inputEl().removeClass(['hide', 'x-hidden']);
26387 this.inputEl().dom.removeAttribute('tabIndex');
26388 this.inputEl().focus();
26390 Roo.log('editor - hiding textarea');
26392 // Roo.log(this.pushValue());
26395 this.inputEl().addClass(['hide', 'x-hidden']);
26396 this.inputEl().dom.setAttribute('tabIndex', -1);
26397 //this.deferFocus();
26400 if(this.resizable){
26401 this.setSize(this.wrap.getSize());
26404 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26407 // private (for BoxComponent)
26408 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26410 // private (for BoxComponent)
26411 getResizeEl : function(){
26415 // private (for BoxComponent)
26416 getPositionEl : function(){
26421 initEvents : function(){
26422 this.originalValue = this.getValue();
26426 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26429 // markInvalid : Roo.emptyFn,
26431 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26434 // clearInvalid : Roo.emptyFn,
26436 setValue : function(v){
26437 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26438 this.editorcore.pushValue();
26443 deferFocus : function(){
26444 this.focus.defer(10, this);
26448 focus : function(){
26449 this.editorcore.focus();
26455 onDestroy : function(){
26461 for (var i =0; i < this.toolbars.length;i++) {
26462 // fixme - ask toolbars for heights?
26463 this.toolbars[i].onDestroy();
26466 this.wrap.dom.innerHTML = '';
26467 this.wrap.remove();
26472 onFirstFocus : function(){
26473 //Roo.log("onFirstFocus");
26474 this.editorcore.onFirstFocus();
26475 for (var i =0; i < this.toolbars.length;i++) {
26476 this.toolbars[i].onFirstFocus();
26482 syncValue : function()
26484 this.editorcore.syncValue();
26487 pushValue : function()
26489 this.editorcore.pushValue();
26493 // hide stuff that is not compatible
26507 * @event specialkey
26511 * @cfg {String} fieldClass @hide
26514 * @cfg {String} focusClass @hide
26517 * @cfg {String} autoCreate @hide
26520 * @cfg {String} inputType @hide
26524 * @cfg {String} invalidText @hide
26527 * @cfg {String} msgFx @hide
26530 * @cfg {String} validateOnBlur @hide
26539 Roo.namespace('Roo.bootstrap.htmleditor');
26541 * @class Roo.bootstrap.HtmlEditorToolbar1
26547 new Roo.bootstrap.HtmlEditor({
26550 new Roo.bootstrap.HtmlEditorToolbar1({
26551 disable : { fonts: 1 , format: 1, ..., ... , ...],
26557 * @cfg {Object} disable List of elements to disable..
26558 * @cfg {Array} btns List of additional buttons.
26562 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26565 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26568 Roo.apply(this, config);
26570 // default disabled, based on 'good practice'..
26571 this.disable = this.disable || {};
26572 Roo.applyIf(this.disable, {
26575 specialElements : true
26577 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26579 this.editor = config.editor;
26580 this.editorcore = config.editor.editorcore;
26582 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26584 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26585 // dont call parent... till later.
26587 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26592 editorcore : false,
26597 "h1","h2","h3","h4","h5","h6",
26599 "abbr", "acronym", "address", "cite", "samp", "var",
26603 onRender : function(ct, position)
26605 // Roo.log("Call onRender: " + this.xtype);
26607 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26609 this.el.dom.style.marginBottom = '0';
26611 var editorcore = this.editorcore;
26612 var editor= this.editor;
26615 var btn = function(id,cmd , toggle, handler, html){
26617 var event = toggle ? 'toggle' : 'click';
26622 xns: Roo.bootstrap,
26626 enableToggle:toggle !== false,
26628 pressed : toggle ? false : null,
26631 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26632 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26638 // var cb_box = function...
26643 xns: Roo.bootstrap,
26648 xns: Roo.bootstrap,
26652 Roo.each(this.formats, function(f) {
26653 style.menu.items.push({
26655 xns: Roo.bootstrap,
26656 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26661 editorcore.insertTag(this.tagname);
26668 children.push(style);
26670 btn('bold',false,true);
26671 btn('italic',false,true);
26672 btn('align-left', 'justifyleft',true);
26673 btn('align-center', 'justifycenter',true);
26674 btn('align-right' , 'justifyright',true);
26675 btn('link', false, false, function(btn) {
26676 //Roo.log("create link?");
26677 var url = prompt(this.createLinkText, this.defaultLinkValue);
26678 if(url && url != 'http:/'+'/'){
26679 this.editorcore.relayCmd('createlink', url);
26682 btn('list','insertunorderedlist',true);
26683 btn('pencil', false,true, function(btn){
26685 this.toggleSourceEdit(btn.pressed);
26688 if (this.editor.btns.length > 0) {
26689 for (var i = 0; i<this.editor.btns.length; i++) {
26690 children.push(this.editor.btns[i]);
26698 xns: Roo.bootstrap,
26703 xns: Roo.bootstrap,
26708 cog.menu.items.push({
26710 xns: Roo.bootstrap,
26711 html : Clean styles,
26716 editorcore.insertTag(this.tagname);
26725 this.xtype = 'NavSimplebar';
26727 for(var i=0;i< children.length;i++) {
26729 this.buttons.add(this.addxtypeChild(children[i]));
26733 editor.on('editorevent', this.updateToolbar, this);
26735 onBtnClick : function(id)
26737 this.editorcore.relayCmd(id);
26738 this.editorcore.focus();
26742 * Protected method that will not generally be called directly. It triggers
26743 * a toolbar update by reading the markup state of the current selection in the editor.
26745 updateToolbar: function(){
26747 if(!this.editorcore.activated){
26748 this.editor.onFirstFocus(); // is this neeed?
26752 var btns = this.buttons;
26753 var doc = this.editorcore.doc;
26754 btns.get('bold').setActive(doc.queryCommandState('bold'));
26755 btns.get('italic').setActive(doc.queryCommandState('italic'));
26756 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26758 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26759 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26760 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26762 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26763 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26766 var ans = this.editorcore.getAllAncestors();
26767 if (this.formatCombo) {
26770 var store = this.formatCombo.store;
26771 this.formatCombo.setValue("");
26772 for (var i =0; i < ans.length;i++) {
26773 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26775 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26783 // hides menus... - so this cant be on a menu...
26784 Roo.bootstrap.MenuMgr.hideAll();
26786 Roo.bootstrap.MenuMgr.hideAll();
26787 //this.editorsyncValue();
26789 onFirstFocus: function() {
26790 this.buttons.each(function(item){
26794 toggleSourceEdit : function(sourceEditMode){
26797 if(sourceEditMode){
26798 Roo.log("disabling buttons");
26799 this.buttons.each( function(item){
26800 if(item.cmd != 'pencil'){
26806 Roo.log("enabling buttons");
26807 if(this.editorcore.initialized){
26808 this.buttons.each( function(item){
26814 Roo.log("calling toggole on editor");
26815 // tell the editor that it's been pressed..
26816 this.editor.toggleSourceEdit(sourceEditMode);
26830 * @class Roo.bootstrap.Markdown
26831 * @extends Roo.bootstrap.TextArea
26832 * Bootstrap Showdown editable area
26833 * @cfg {string} content
26836 * Create a new Showdown
26839 Roo.bootstrap.Markdown = function(config){
26840 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26844 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26848 initEvents : function()
26851 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26852 this.markdownEl = this.el.createChild({
26853 cls : 'roo-markdown-area'
26855 this.inputEl().addClass('d-none');
26856 if (this.getValue() == '') {
26857 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26860 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26862 this.markdownEl.on('click', this.toggleTextEdit, this);
26863 this.on('blur', this.toggleTextEdit, this);
26864 this.on('specialkey', this.resizeTextArea, this);
26867 toggleTextEdit : function()
26869 var sh = this.markdownEl.getHeight();
26870 this.inputEl().addClass('d-none');
26871 this.markdownEl.addClass('d-none');
26872 if (!this.editing) {
26874 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26875 this.inputEl().removeClass('d-none');
26876 this.inputEl().focus();
26877 this.editing = true;
26880 // show showdown...
26881 this.updateMarkdown();
26882 this.markdownEl.removeClass('d-none');
26883 this.editing = false;
26886 updateMarkdown : function()
26888 if (this.getValue() == '') {
26889 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26893 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26896 resizeTextArea: function () {
26899 Roo.log([sh, this.getValue().split("\n").length * 30]);
26900 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26902 setValue : function(val)
26904 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26905 if (!this.editing) {
26906 this.updateMarkdown();
26912 if (!this.editing) {
26913 this.toggleTextEdit();
26921 * @class Roo.bootstrap.Table.AbstractSelectionModel
26922 * @extends Roo.util.Observable
26923 * Abstract base class for grid SelectionModels. It provides the interface that should be
26924 * implemented by descendant classes. This class should not be directly instantiated.
26927 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26928 this.locked = false;
26929 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26933 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26934 /** @ignore Called by the grid automatically. Do not call directly. */
26935 init : function(grid){
26941 * Locks the selections.
26944 this.locked = true;
26948 * Unlocks the selections.
26950 unlock : function(){
26951 this.locked = false;
26955 * Returns true if the selections are locked.
26956 * @return {Boolean}
26958 isLocked : function(){
26959 return this.locked;
26963 initEvents : function ()
26969 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26970 * @class Roo.bootstrap.Table.RowSelectionModel
26971 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26972 * It supports multiple selections and keyboard selection/navigation.
26974 * @param {Object} config
26977 Roo.bootstrap.Table.RowSelectionModel = function(config){
26978 Roo.apply(this, config);
26979 this.selections = new Roo.util.MixedCollection(false, function(o){
26984 this.lastActive = false;
26988 * @event selectionchange
26989 * Fires when the selection changes
26990 * @param {SelectionModel} this
26992 "selectionchange" : true,
26994 * @event afterselectionchange
26995 * Fires after the selection changes (eg. by key press or clicking)
26996 * @param {SelectionModel} this
26998 "afterselectionchange" : true,
27000 * @event beforerowselect
27001 * Fires when a row is selected being selected, return false to cancel.
27002 * @param {SelectionModel} this
27003 * @param {Number} rowIndex The selected index
27004 * @param {Boolean} keepExisting False if other selections will be cleared
27006 "beforerowselect" : true,
27009 * Fires when a row is selected.
27010 * @param {SelectionModel} this
27011 * @param {Number} rowIndex The selected index
27012 * @param {Roo.data.Record} r The record
27014 "rowselect" : true,
27016 * @event rowdeselect
27017 * Fires when a row is deselected.
27018 * @param {SelectionModel} this
27019 * @param {Number} rowIndex The selected index
27021 "rowdeselect" : true
27023 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27024 this.locked = false;
27027 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27029 * @cfg {Boolean} singleSelect
27030 * True to allow selection of only one row at a time (defaults to false)
27032 singleSelect : false,
27035 initEvents : function()
27038 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27039 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27040 //}else{ // allow click to work like normal
27041 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27043 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27044 this.grid.on("rowclick", this.handleMouseDown, this);
27046 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27047 "up" : function(e){
27049 this.selectPrevious(e.shiftKey);
27050 }else if(this.last !== false && this.lastActive !== false){
27051 var last = this.last;
27052 this.selectRange(this.last, this.lastActive-1);
27053 this.grid.getView().focusRow(this.lastActive);
27054 if(last !== false){
27058 this.selectFirstRow();
27060 this.fireEvent("afterselectionchange", this);
27062 "down" : function(e){
27064 this.selectNext(e.shiftKey);
27065 }else if(this.last !== false && this.lastActive !== false){
27066 var last = this.last;
27067 this.selectRange(this.last, this.lastActive+1);
27068 this.grid.getView().focusRow(this.lastActive);
27069 if(last !== false){
27073 this.selectFirstRow();
27075 this.fireEvent("afterselectionchange", this);
27079 this.grid.store.on('load', function(){
27080 this.selections.clear();
27083 var view = this.grid.view;
27084 view.on("refresh", this.onRefresh, this);
27085 view.on("rowupdated", this.onRowUpdated, this);
27086 view.on("rowremoved", this.onRemove, this);
27091 onRefresh : function()
27093 var ds = this.grid.store, i, v = this.grid.view;
27094 var s = this.selections;
27095 s.each(function(r){
27096 if((i = ds.indexOfId(r.id)) != -1){
27105 onRemove : function(v, index, r){
27106 this.selections.remove(r);
27110 onRowUpdated : function(v, index, r){
27111 if(this.isSelected(r)){
27112 v.onRowSelect(index);
27118 * @param {Array} records The records to select
27119 * @param {Boolean} keepExisting (optional) True to keep existing selections
27121 selectRecords : function(records, keepExisting)
27124 this.clearSelections();
27126 var ds = this.grid.store;
27127 for(var i = 0, len = records.length; i < len; i++){
27128 this.selectRow(ds.indexOf(records[i]), true);
27133 * Gets the number of selected rows.
27136 getCount : function(){
27137 return this.selections.length;
27141 * Selects the first row in the grid.
27143 selectFirstRow : function(){
27148 * Select the last row.
27149 * @param {Boolean} keepExisting (optional) True to keep existing selections
27151 selectLastRow : function(keepExisting){
27152 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27153 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27157 * Selects the row immediately following the last selected row.
27158 * @param {Boolean} keepExisting (optional) True to keep existing selections
27160 selectNext : function(keepExisting)
27162 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27163 this.selectRow(this.last+1, keepExisting);
27164 this.grid.getView().focusRow(this.last);
27169 * Selects the row that precedes the last selected row.
27170 * @param {Boolean} keepExisting (optional) True to keep existing selections
27172 selectPrevious : function(keepExisting){
27174 this.selectRow(this.last-1, keepExisting);
27175 this.grid.getView().focusRow(this.last);
27180 * Returns the selected records
27181 * @return {Array} Array of selected records
27183 getSelections : function(){
27184 return [].concat(this.selections.items);
27188 * Returns the first selected record.
27191 getSelected : function(){
27192 return this.selections.itemAt(0);
27197 * Clears all selections.
27199 clearSelections : function(fast)
27205 var ds = this.grid.store;
27206 var s = this.selections;
27207 s.each(function(r){
27208 this.deselectRow(ds.indexOfId(r.id));
27212 this.selections.clear();
27219 * Selects all rows.
27221 selectAll : function(){
27225 this.selections.clear();
27226 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27227 this.selectRow(i, true);
27232 * Returns True if there is a selection.
27233 * @return {Boolean}
27235 hasSelection : function(){
27236 return this.selections.length > 0;
27240 * Returns True if the specified row is selected.
27241 * @param {Number/Record} record The record or index of the record to check
27242 * @return {Boolean}
27244 isSelected : function(index){
27245 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27246 return (r && this.selections.key(r.id) ? true : false);
27250 * Returns True if the specified record id is selected.
27251 * @param {String} id The id of record to check
27252 * @return {Boolean}
27254 isIdSelected : function(id){
27255 return (this.selections.key(id) ? true : false);
27260 handleMouseDBClick : function(e, t){
27264 handleMouseDown : function(e, t)
27266 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27267 if(this.isLocked() || rowIndex < 0 ){
27270 if(e.shiftKey && this.last !== false){
27271 var last = this.last;
27272 this.selectRange(last, rowIndex, e.ctrlKey);
27273 this.last = last; // reset the last
27277 var isSelected = this.isSelected(rowIndex);
27278 //Roo.log("select row:" + rowIndex);
27280 this.deselectRow(rowIndex);
27282 this.selectRow(rowIndex, true);
27286 if(e.button !== 0 && isSelected){
27287 alert('rowIndex 2: ' + rowIndex);
27288 view.focusRow(rowIndex);
27289 }else if(e.ctrlKey && isSelected){
27290 this.deselectRow(rowIndex);
27291 }else if(!isSelected){
27292 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27293 view.focusRow(rowIndex);
27297 this.fireEvent("afterselectionchange", this);
27300 handleDragableRowClick : function(grid, rowIndex, e)
27302 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27303 this.selectRow(rowIndex, false);
27304 grid.view.focusRow(rowIndex);
27305 this.fireEvent("afterselectionchange", this);
27310 * Selects multiple rows.
27311 * @param {Array} rows Array of the indexes of the row to select
27312 * @param {Boolean} keepExisting (optional) True to keep existing selections
27314 selectRows : function(rows, keepExisting){
27316 this.clearSelections();
27318 for(var i = 0, len = rows.length; i < len; i++){
27319 this.selectRow(rows[i], true);
27324 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27325 * @param {Number} startRow The index of the first row in the range
27326 * @param {Number} endRow The index of the last row in the range
27327 * @param {Boolean} keepExisting (optional) True to retain existing selections
27329 selectRange : function(startRow, endRow, keepExisting){
27334 this.clearSelections();
27336 if(startRow <= endRow){
27337 for(var i = startRow; i <= endRow; i++){
27338 this.selectRow(i, true);
27341 for(var i = startRow; i >= endRow; i--){
27342 this.selectRow(i, true);
27348 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27349 * @param {Number} startRow The index of the first row in the range
27350 * @param {Number} endRow The index of the last row in the range
27352 deselectRange : function(startRow, endRow, preventViewNotify){
27356 for(var i = startRow; i <= endRow; i++){
27357 this.deselectRow(i, preventViewNotify);
27363 * @param {Number} row The index of the row to select
27364 * @param {Boolean} keepExisting (optional) True to keep existing selections
27366 selectRow : function(index, keepExisting, preventViewNotify)
27368 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27371 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27372 if(!keepExisting || this.singleSelect){
27373 this.clearSelections();
27376 var r = this.grid.store.getAt(index);
27377 //console.log('selectRow - record id :' + r.id);
27379 this.selections.add(r);
27380 this.last = this.lastActive = index;
27381 if(!preventViewNotify){
27382 var proxy = new Roo.Element(
27383 this.grid.getRowDom(index)
27385 proxy.addClass('bg-info info');
27387 this.fireEvent("rowselect", this, index, r);
27388 this.fireEvent("selectionchange", this);
27394 * @param {Number} row The index of the row to deselect
27396 deselectRow : function(index, preventViewNotify)
27401 if(this.last == index){
27404 if(this.lastActive == index){
27405 this.lastActive = false;
27408 var r = this.grid.store.getAt(index);
27413 this.selections.remove(r);
27414 //.console.log('deselectRow - record id :' + r.id);
27415 if(!preventViewNotify){
27417 var proxy = new Roo.Element(
27418 this.grid.getRowDom(index)
27420 proxy.removeClass('bg-info info');
27422 this.fireEvent("rowdeselect", this, index);
27423 this.fireEvent("selectionchange", this);
27427 restoreLast : function(){
27429 this.last = this._last;
27434 acceptsNav : function(row, col, cm){
27435 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27439 onEditorKey : function(field, e){
27440 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27445 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27447 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27449 }else if(k == e.ENTER && !e.ctrlKey){
27453 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27455 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27457 }else if(k == e.ESC){
27461 g.startEditing(newCell[0], newCell[1]);
27467 * Ext JS Library 1.1.1
27468 * Copyright(c) 2006-2007, Ext JS, LLC.
27470 * Originally Released Under LGPL - original licence link has changed is not relivant.
27473 * <script type="text/javascript">
27477 * @class Roo.bootstrap.PagingToolbar
27478 * @extends Roo.bootstrap.NavSimplebar
27479 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27481 * Create a new PagingToolbar
27482 * @param {Object} config The config object
27483 * @param {Roo.data.Store} store
27485 Roo.bootstrap.PagingToolbar = function(config)
27487 // old args format still supported... - xtype is prefered..
27488 // created from xtype...
27490 this.ds = config.dataSource;
27492 if (config.store && !this.ds) {
27493 this.store= Roo.factory(config.store, Roo.data);
27494 this.ds = this.store;
27495 this.ds.xmodule = this.xmodule || false;
27498 this.toolbarItems = [];
27499 if (config.items) {
27500 this.toolbarItems = config.items;
27503 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27508 this.bind(this.ds);
27511 if (Roo.bootstrap.version == 4) {
27512 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27514 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27519 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27521 * @cfg {Roo.data.Store} dataSource
27522 * The underlying data store providing the paged data
27525 * @cfg {String/HTMLElement/Element} container
27526 * container The id or element that will contain the toolbar
27529 * @cfg {Boolean} displayInfo
27530 * True to display the displayMsg (defaults to false)
27533 * @cfg {Number} pageSize
27534 * The number of records to display per page (defaults to 20)
27538 * @cfg {String} displayMsg
27539 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27541 displayMsg : 'Displaying {0} - {1} of {2}',
27543 * @cfg {String} emptyMsg
27544 * The message to display when no records are found (defaults to "No data to display")
27546 emptyMsg : 'No data to display',
27548 * Customizable piece of the default paging text (defaults to "Page")
27551 beforePageText : "Page",
27553 * Customizable piece of the default paging text (defaults to "of %0")
27556 afterPageText : "of {0}",
27558 * Customizable piece of the default paging text (defaults to "First Page")
27561 firstText : "First Page",
27563 * Customizable piece of the default paging text (defaults to "Previous Page")
27566 prevText : "Previous Page",
27568 * Customizable piece of the default paging text (defaults to "Next Page")
27571 nextText : "Next Page",
27573 * Customizable piece of the default paging text (defaults to "Last Page")
27576 lastText : "Last Page",
27578 * Customizable piece of the default paging text (defaults to "Refresh")
27581 refreshText : "Refresh",
27585 onRender : function(ct, position)
27587 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27588 this.navgroup.parentId = this.id;
27589 this.navgroup.onRender(this.el, null);
27590 // add the buttons to the navgroup
27592 if(this.displayInfo){
27593 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27594 this.displayEl = this.el.select('.x-paging-info', true).first();
27595 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27596 // this.displayEl = navel.el.select('span',true).first();
27602 Roo.each(_this.buttons, function(e){ // this might need to use render????
27603 Roo.factory(e).render(_this.el);
27607 Roo.each(_this.toolbarItems, function(e) {
27608 _this.navgroup.addItem(e);
27612 this.first = this.navgroup.addItem({
27613 tooltip: this.firstText,
27614 cls: "prev btn-outline-secondary",
27615 html : ' <i class="fa fa-step-backward"></i>',
27617 preventDefault: true,
27618 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27621 this.prev = this.navgroup.addItem({
27622 tooltip: this.prevText,
27623 cls: "prev btn-outline-secondary",
27624 html : ' <i class="fa fa-backward"></i>',
27626 preventDefault: true,
27627 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27629 //this.addSeparator();
27632 var field = this.navgroup.addItem( {
27634 cls : 'x-paging-position btn-outline-secondary',
27636 html : this.beforePageText +
27637 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27638 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27641 this.field = field.el.select('input', true).first();
27642 this.field.on("keydown", this.onPagingKeydown, this);
27643 this.field.on("focus", function(){this.dom.select();});
27646 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27647 //this.field.setHeight(18);
27648 //this.addSeparator();
27649 this.next = this.navgroup.addItem({
27650 tooltip: this.nextText,
27651 cls: "next btn-outline-secondary",
27652 html : ' <i class="fa fa-forward"></i>',
27654 preventDefault: true,
27655 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27657 this.last = this.navgroup.addItem({
27658 tooltip: this.lastText,
27659 html : ' <i class="fa fa-step-forward"></i>',
27660 cls: "next btn-outline-secondary",
27662 preventDefault: true,
27663 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27665 //this.addSeparator();
27666 this.loading = this.navgroup.addItem({
27667 tooltip: this.refreshText,
27668 cls: "btn-outline-secondary",
27669 html : ' <i class="fa fa-refresh"></i>',
27670 preventDefault: true,
27671 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27677 updateInfo : function(){
27678 if(this.displayEl){
27679 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27680 var msg = count == 0 ?
27684 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27686 this.displayEl.update(msg);
27691 onLoad : function(ds, r, o)
27693 this.cursor = o.params && o.params.start ? o.params.start : 0;
27695 var d = this.getPageData(),
27700 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27701 this.field.dom.value = ap;
27702 this.first.setDisabled(ap == 1);
27703 this.prev.setDisabled(ap == 1);
27704 this.next.setDisabled(ap == ps);
27705 this.last.setDisabled(ap == ps);
27706 this.loading.enable();
27711 getPageData : function(){
27712 var total = this.ds.getTotalCount();
27715 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27716 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27721 onLoadError : function(){
27722 this.loading.enable();
27726 onPagingKeydown : function(e){
27727 var k = e.getKey();
27728 var d = this.getPageData();
27730 var v = this.field.dom.value, pageNum;
27731 if(!v || isNaN(pageNum = parseInt(v, 10))){
27732 this.field.dom.value = d.activePage;
27735 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27736 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27739 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))
27741 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27742 this.field.dom.value = pageNum;
27743 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27746 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27748 var v = this.field.dom.value, pageNum;
27749 var increment = (e.shiftKey) ? 10 : 1;
27750 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27753 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27754 this.field.dom.value = d.activePage;
27757 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27759 this.field.dom.value = parseInt(v, 10) + increment;
27760 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27761 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27768 beforeLoad : function(){
27770 this.loading.disable();
27775 onClick : function(which){
27784 ds.load({params:{start: 0, limit: this.pageSize}});
27787 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27790 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27793 var total = ds.getTotalCount();
27794 var extra = total % this.pageSize;
27795 var lastStart = extra ? (total - extra) : total-this.pageSize;
27796 ds.load({params:{start: lastStart, limit: this.pageSize}});
27799 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27805 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27806 * @param {Roo.data.Store} store The data store to unbind
27808 unbind : function(ds){
27809 ds.un("beforeload", this.beforeLoad, this);
27810 ds.un("load", this.onLoad, this);
27811 ds.un("loadexception", this.onLoadError, this);
27812 ds.un("remove", this.updateInfo, this);
27813 ds.un("add", this.updateInfo, this);
27814 this.ds = undefined;
27818 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27819 * @param {Roo.data.Store} store The data store to bind
27821 bind : function(ds){
27822 ds.on("beforeload", this.beforeLoad, this);
27823 ds.on("load", this.onLoad, this);
27824 ds.on("loadexception", this.onLoadError, this);
27825 ds.on("remove", this.updateInfo, this);
27826 ds.on("add", this.updateInfo, this);
27837 * @class Roo.bootstrap.MessageBar
27838 * @extends Roo.bootstrap.Component
27839 * Bootstrap MessageBar class
27840 * @cfg {String} html contents of the MessageBar
27841 * @cfg {String} weight (info | success | warning | danger) default info
27842 * @cfg {String} beforeClass insert the bar before the given class
27843 * @cfg {Boolean} closable (true | false) default false
27844 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27847 * Create a new Element
27848 * @param {Object} config The config object
27851 Roo.bootstrap.MessageBar = function(config){
27852 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27855 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27861 beforeClass: 'bootstrap-sticky-wrap',
27863 getAutoCreate : function(){
27867 cls: 'alert alert-dismissable alert-' + this.weight,
27872 html: this.html || ''
27878 cfg.cls += ' alert-messages-fixed';
27892 onRender : function(ct, position)
27894 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27897 var cfg = Roo.apply({}, this.getAutoCreate());
27901 cfg.cls += ' ' + this.cls;
27904 cfg.style = this.style;
27906 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27908 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27911 this.el.select('>button.close').on('click', this.hide, this);
27917 if (!this.rendered) {
27923 this.fireEvent('show', this);
27929 if (!this.rendered) {
27935 this.fireEvent('hide', this);
27938 update : function()
27940 // var e = this.el.dom.firstChild;
27942 // if(this.closable){
27943 // e = e.nextSibling;
27946 // e.data = this.html || '';
27948 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27964 * @class Roo.bootstrap.Graph
27965 * @extends Roo.bootstrap.Component
27966 * Bootstrap Graph class
27970 @cfg {String} graphtype bar | vbar | pie
27971 @cfg {number} g_x coodinator | centre x (pie)
27972 @cfg {number} g_y coodinator | centre y (pie)
27973 @cfg {number} g_r radius (pie)
27974 @cfg {number} g_height height of the chart (respected by all elements in the set)
27975 @cfg {number} g_width width of the chart (respected by all elements in the set)
27976 @cfg {Object} title The title of the chart
27979 -opts (object) options for the chart
27981 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27982 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27984 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.
27985 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27987 o stretch (boolean)
27989 -opts (object) options for the pie
27992 o startAngle (number)
27993 o endAngle (number)
27997 * Create a new Input
27998 * @param {Object} config The config object
28001 Roo.bootstrap.Graph = function(config){
28002 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28008 * The img click event for the img.
28009 * @param {Roo.EventObject} e
28015 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28026 //g_colors: this.colors,
28033 getAutoCreate : function(){
28044 onRender : function(ct,position){
28047 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28049 if (typeof(Raphael) == 'undefined') {
28050 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28054 this.raphael = Raphael(this.el.dom);
28056 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28057 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28058 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28059 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28061 r.text(160, 10, "Single Series Chart").attr(txtattr);
28062 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28063 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28064 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28066 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28067 r.barchart(330, 10, 300, 220, data1);
28068 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28069 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28072 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28073 // r.barchart(30, 30, 560, 250, xdata, {
28074 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28075 // axis : "0 0 1 1",
28076 // axisxlabels : xdata
28077 // //yvalues : cols,
28080 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28082 // this.load(null,xdata,{
28083 // axis : "0 0 1 1",
28084 // axisxlabels : xdata
28089 load : function(graphtype,xdata,opts)
28091 this.raphael.clear();
28093 graphtype = this.graphtype;
28098 var r = this.raphael,
28099 fin = function () {
28100 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28102 fout = function () {
28103 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28105 pfin = function() {
28106 this.sector.stop();
28107 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28110 this.label[0].stop();
28111 this.label[0].attr({ r: 7.5 });
28112 this.label[1].attr({ "font-weight": 800 });
28115 pfout = function() {
28116 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28119 this.label[0].animate({ r: 5 }, 500, "bounce");
28120 this.label[1].attr({ "font-weight": 400 });
28126 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28129 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28132 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28133 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28135 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28142 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28147 setTitle: function(o)
28152 initEvents: function() {
28155 this.el.on('click', this.onClick, this);
28159 onClick : function(e)
28161 Roo.log('img onclick');
28162 this.fireEvent('click', this, e);
28174 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28177 * @class Roo.bootstrap.dash.NumberBox
28178 * @extends Roo.bootstrap.Component
28179 * Bootstrap NumberBox class
28180 * @cfg {String} headline Box headline
28181 * @cfg {String} content Box content
28182 * @cfg {String} icon Box icon
28183 * @cfg {String} footer Footer text
28184 * @cfg {String} fhref Footer href
28187 * Create a new NumberBox
28188 * @param {Object} config The config object
28192 Roo.bootstrap.dash.NumberBox = function(config){
28193 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28197 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28206 getAutoCreate : function(){
28210 cls : 'small-box ',
28218 cls : 'roo-headline',
28219 html : this.headline
28223 cls : 'roo-content',
28224 html : this.content
28238 cls : 'ion ' + this.icon
28247 cls : 'small-box-footer',
28248 href : this.fhref || '#',
28252 cfg.cn.push(footer);
28259 onRender : function(ct,position){
28260 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28267 setHeadline: function (value)
28269 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28272 setFooter: function (value, href)
28274 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28277 this.el.select('a.small-box-footer',true).first().attr('href', href);
28282 setContent: function (value)
28284 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28287 initEvents: function()
28301 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28304 * @class Roo.bootstrap.dash.TabBox
28305 * @extends Roo.bootstrap.Component
28306 * Bootstrap TabBox class
28307 * @cfg {String} title Title of the TabBox
28308 * @cfg {String} icon Icon of the TabBox
28309 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28310 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28313 * Create a new TabBox
28314 * @param {Object} config The config object
28318 Roo.bootstrap.dash.TabBox = function(config){
28319 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28324 * When a pane is added
28325 * @param {Roo.bootstrap.dash.TabPane} pane
28329 * @event activatepane
28330 * When a pane is activated
28331 * @param {Roo.bootstrap.dash.TabPane} pane
28333 "activatepane" : true
28341 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28346 tabScrollable : false,
28348 getChildContainer : function()
28350 return this.el.select('.tab-content', true).first();
28353 getAutoCreate : function(){
28357 cls: 'pull-left header',
28365 cls: 'fa ' + this.icon
28371 cls: 'nav nav-tabs pull-right',
28377 if(this.tabScrollable){
28384 cls: 'nav nav-tabs pull-right',
28395 cls: 'nav-tabs-custom',
28400 cls: 'tab-content no-padding',
28408 initEvents : function()
28410 //Roo.log('add add pane handler');
28411 this.on('addpane', this.onAddPane, this);
28414 * Updates the box title
28415 * @param {String} html to set the title to.
28417 setTitle : function(value)
28419 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28421 onAddPane : function(pane)
28423 this.panes.push(pane);
28424 //Roo.log('addpane');
28426 // tabs are rendere left to right..
28427 if(!this.showtabs){
28431 var ctr = this.el.select('.nav-tabs', true).first();
28434 var existing = ctr.select('.nav-tab',true);
28435 var qty = existing.getCount();;
28438 var tab = ctr.createChild({
28440 cls : 'nav-tab' + (qty ? '' : ' active'),
28448 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28451 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28453 pane.el.addClass('active');
28458 onTabClick : function(ev,un,ob,pane)
28460 //Roo.log('tab - prev default');
28461 ev.preventDefault();
28464 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28465 pane.tab.addClass('active');
28466 //Roo.log(pane.title);
28467 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28468 // technically we should have a deactivate event.. but maybe add later.
28469 // and it should not de-activate the selected tab...
28470 this.fireEvent('activatepane', pane);
28471 pane.el.addClass('active');
28472 pane.fireEvent('activate');
28477 getActivePane : function()
28480 Roo.each(this.panes, function(p) {
28481 if(p.el.hasClass('active')){
28502 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28504 * @class Roo.bootstrap.TabPane
28505 * @extends Roo.bootstrap.Component
28506 * Bootstrap TabPane class
28507 * @cfg {Boolean} active (false | true) Default false
28508 * @cfg {String} title title of panel
28512 * Create a new TabPane
28513 * @param {Object} config The config object
28516 Roo.bootstrap.dash.TabPane = function(config){
28517 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28523 * When a pane is activated
28524 * @param {Roo.bootstrap.dash.TabPane} pane
28531 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28536 // the tabBox that this is attached to.
28539 getAutoCreate : function()
28547 cfg.cls += ' active';
28552 initEvents : function()
28554 //Roo.log('trigger add pane handler');
28555 this.parent().fireEvent('addpane', this)
28559 * Updates the tab title
28560 * @param {String} html to set the title to.
28562 setTitle: function(str)
28568 this.tab.select('a', true).first().dom.innerHTML = str;
28585 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28588 * @class Roo.bootstrap.menu.Menu
28589 * @extends Roo.bootstrap.Component
28590 * Bootstrap Menu class - container for Menu
28591 * @cfg {String} html Text of the menu
28592 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28593 * @cfg {String} icon Font awesome icon
28594 * @cfg {String} pos Menu align to (top | bottom) default bottom
28598 * Create a new Menu
28599 * @param {Object} config The config object
28603 Roo.bootstrap.menu.Menu = function(config){
28604 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28608 * @event beforeshow
28609 * Fires before this menu is displayed
28610 * @param {Roo.bootstrap.menu.Menu} this
28614 * @event beforehide
28615 * Fires before this menu is hidden
28616 * @param {Roo.bootstrap.menu.Menu} this
28621 * Fires after this menu is displayed
28622 * @param {Roo.bootstrap.menu.Menu} this
28627 * Fires after this menu is hidden
28628 * @param {Roo.bootstrap.menu.Menu} this
28633 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28634 * @param {Roo.bootstrap.menu.Menu} this
28635 * @param {Roo.EventObject} e
28642 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28646 weight : 'default',
28651 getChildContainer : function() {
28652 if(this.isSubMenu){
28656 return this.el.select('ul.dropdown-menu', true).first();
28659 getAutoCreate : function()
28664 cls : 'roo-menu-text',
28672 cls : 'fa ' + this.icon
28683 cls : 'dropdown-button btn btn-' + this.weight,
28688 cls : 'dropdown-toggle btn btn-' + this.weight,
28698 cls : 'dropdown-menu'
28704 if(this.pos == 'top'){
28705 cfg.cls += ' dropup';
28708 if(this.isSubMenu){
28711 cls : 'dropdown-menu'
28718 onRender : function(ct, position)
28720 this.isSubMenu = ct.hasClass('dropdown-submenu');
28722 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28725 initEvents : function()
28727 if(this.isSubMenu){
28731 this.hidden = true;
28733 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28734 this.triggerEl.on('click', this.onTriggerPress, this);
28736 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28737 this.buttonEl.on('click', this.onClick, this);
28743 if(this.isSubMenu){
28747 return this.el.select('ul.dropdown-menu', true).first();
28750 onClick : function(e)
28752 this.fireEvent("click", this, e);
28755 onTriggerPress : function(e)
28757 if (this.isVisible()) {
28764 isVisible : function(){
28765 return !this.hidden;
28770 this.fireEvent("beforeshow", this);
28772 this.hidden = false;
28773 this.el.addClass('open');
28775 Roo.get(document).on("mouseup", this.onMouseUp, this);
28777 this.fireEvent("show", this);
28784 this.fireEvent("beforehide", this);
28786 this.hidden = true;
28787 this.el.removeClass('open');
28789 Roo.get(document).un("mouseup", this.onMouseUp);
28791 this.fireEvent("hide", this);
28794 onMouseUp : function()
28808 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28811 * @class Roo.bootstrap.menu.Item
28812 * @extends Roo.bootstrap.Component
28813 * Bootstrap MenuItem class
28814 * @cfg {Boolean} submenu (true | false) default false
28815 * @cfg {String} html text of the item
28816 * @cfg {String} href the link
28817 * @cfg {Boolean} disable (true | false) default false
28818 * @cfg {Boolean} preventDefault (true | false) default true
28819 * @cfg {String} icon Font awesome icon
28820 * @cfg {String} pos Submenu align to (left | right) default right
28824 * Create a new Item
28825 * @param {Object} config The config object
28829 Roo.bootstrap.menu.Item = function(config){
28830 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28834 * Fires when the mouse is hovering over this menu
28835 * @param {Roo.bootstrap.menu.Item} this
28836 * @param {Roo.EventObject} e
28841 * Fires when the mouse exits this menu
28842 * @param {Roo.bootstrap.menu.Item} this
28843 * @param {Roo.EventObject} e
28849 * The raw click event for the entire grid.
28850 * @param {Roo.EventObject} e
28856 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28861 preventDefault: true,
28866 getAutoCreate : function()
28871 cls : 'roo-menu-item-text',
28879 cls : 'fa ' + this.icon
28888 href : this.href || '#',
28895 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28899 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28901 if(this.pos == 'left'){
28902 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28909 initEvents : function()
28911 this.el.on('mouseover', this.onMouseOver, this);
28912 this.el.on('mouseout', this.onMouseOut, this);
28914 this.el.select('a', true).first().on('click', this.onClick, this);
28918 onClick : function(e)
28920 if(this.preventDefault){
28921 e.preventDefault();
28924 this.fireEvent("click", this, e);
28927 onMouseOver : function(e)
28929 if(this.submenu && this.pos == 'left'){
28930 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28933 this.fireEvent("mouseover", this, e);
28936 onMouseOut : function(e)
28938 this.fireEvent("mouseout", this, e);
28950 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28953 * @class Roo.bootstrap.menu.Separator
28954 * @extends Roo.bootstrap.Component
28955 * Bootstrap Separator class
28958 * Create a new Separator
28959 * @param {Object} config The config object
28963 Roo.bootstrap.menu.Separator = function(config){
28964 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28967 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28969 getAutoCreate : function(){
28972 cls: 'dropdown-divider divider'
28990 * @class Roo.bootstrap.Tooltip
28991 * Bootstrap Tooltip class
28992 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28993 * to determine which dom element triggers the tooltip.
28995 * It needs to add support for additional attributes like tooltip-position
28998 * Create a new Toolti
28999 * @param {Object} config The config object
29002 Roo.bootstrap.Tooltip = function(config){
29003 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29005 this.alignment = Roo.bootstrap.Tooltip.alignment;
29007 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29008 this.alignment = config.alignment;
29013 Roo.apply(Roo.bootstrap.Tooltip, {
29015 * @function init initialize tooltip monitoring.
29019 currentTip : false,
29020 currentRegion : false,
29026 Roo.get(document).on('mouseover', this.enter ,this);
29027 Roo.get(document).on('mouseout', this.leave, this);
29030 this.currentTip = new Roo.bootstrap.Tooltip();
29033 enter : function(ev)
29035 var dom = ev.getTarget();
29037 //Roo.log(['enter',dom]);
29038 var el = Roo.fly(dom);
29039 if (this.currentEl) {
29041 //Roo.log(this.currentEl);
29042 //Roo.log(this.currentEl.contains(dom));
29043 if (this.currentEl == el) {
29046 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29052 if (this.currentTip.el) {
29053 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29057 if(!el || el.dom == document){
29063 // you can not look for children, as if el is the body.. then everythign is the child..
29064 if (!el.attr('tooltip')) { //
29065 if (!el.select("[tooltip]").elements.length) {
29068 // is the mouse over this child...?
29069 bindEl = el.select("[tooltip]").first();
29070 var xy = ev.getXY();
29071 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29072 //Roo.log("not in region.");
29075 //Roo.log("child element over..");
29078 this.currentEl = bindEl;
29079 this.currentTip.bind(bindEl);
29080 this.currentRegion = Roo.lib.Region.getRegion(dom);
29081 this.currentTip.enter();
29084 leave : function(ev)
29086 var dom = ev.getTarget();
29087 //Roo.log(['leave',dom]);
29088 if (!this.currentEl) {
29093 if (dom != this.currentEl.dom) {
29096 var xy = ev.getXY();
29097 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29100 // only activate leave if mouse cursor is outside... bounding box..
29105 if (this.currentTip) {
29106 this.currentTip.leave();
29108 //Roo.log('clear currentEl');
29109 this.currentEl = false;
29114 'left' : ['r-l', [-2,0], 'right'],
29115 'right' : ['l-r', [2,0], 'left'],
29116 'bottom' : ['t-b', [0,2], 'top'],
29117 'top' : [ 'b-t', [0,-2], 'bottom']
29123 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29128 delay : null, // can be { show : 300 , hide: 500}
29132 hoverState : null, //???
29134 placement : 'bottom',
29138 getAutoCreate : function(){
29145 cls : 'tooltip-arrow arrow'
29148 cls : 'tooltip-inner'
29155 bind : function(el)
29160 initEvents : function()
29162 this.arrowEl = this.el.select('.arrow', true).first();
29163 this.innerEl = this.el.select('.tooltip-inner', true).first();
29166 enter : function () {
29168 if (this.timeout != null) {
29169 clearTimeout(this.timeout);
29172 this.hoverState = 'in';
29173 //Roo.log("enter - show");
29174 if (!this.delay || !this.delay.show) {
29179 this.timeout = setTimeout(function () {
29180 if (_t.hoverState == 'in') {
29183 }, this.delay.show);
29187 clearTimeout(this.timeout);
29189 this.hoverState = 'out';
29190 if (!this.delay || !this.delay.hide) {
29196 this.timeout = setTimeout(function () {
29197 //Roo.log("leave - timeout");
29199 if (_t.hoverState == 'out') {
29201 Roo.bootstrap.Tooltip.currentEl = false;
29206 show : function (msg)
29209 this.render(document.body);
29212 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29214 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29216 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29218 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29219 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29221 var placement = typeof this.placement == 'function' ?
29222 this.placement.call(this, this.el, on_el) :
29225 var autoToken = /\s?auto?\s?/i;
29226 var autoPlace = autoToken.test(placement);
29228 placement = placement.replace(autoToken, '') || 'top';
29232 //this.el.setXY([0,0]);
29234 //this.el.dom.style.display='block';
29236 //this.el.appendTo(on_el);
29238 var p = this.getPosition();
29239 var box = this.el.getBox();
29245 var align = this.alignment[placement];
29247 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29249 if(placement == 'top' || placement == 'bottom'){
29251 placement = 'right';
29254 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29255 placement = 'left';
29258 var scroll = Roo.select('body', true).first().getScroll();
29260 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29264 align = this.alignment[placement];
29266 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29270 this.el.alignTo(this.bindEl, align[0],align[1]);
29271 //var arrow = this.el.select('.arrow',true).first();
29272 //arrow.set(align[2],
29274 this.el.addClass(placement);
29275 this.el.addClass("bs-tooltip-"+ placement);
29277 this.el.addClass('in fade show');
29279 this.hoverState = null;
29281 if (this.el.hasClass('fade')) {
29296 //this.el.setXY([0,0]);
29297 this.el.removeClass(['show', 'in']);
29313 * @class Roo.bootstrap.LocationPicker
29314 * @extends Roo.bootstrap.Component
29315 * Bootstrap LocationPicker class
29316 * @cfg {Number} latitude Position when init default 0
29317 * @cfg {Number} longitude Position when init default 0
29318 * @cfg {Number} zoom default 15
29319 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29320 * @cfg {Boolean} mapTypeControl default false
29321 * @cfg {Boolean} disableDoubleClickZoom default false
29322 * @cfg {Boolean} scrollwheel default true
29323 * @cfg {Boolean} streetViewControl default false
29324 * @cfg {Number} radius default 0
29325 * @cfg {String} locationName
29326 * @cfg {Boolean} draggable default true
29327 * @cfg {Boolean} enableAutocomplete default false
29328 * @cfg {Boolean} enableReverseGeocode default true
29329 * @cfg {String} markerTitle
29332 * Create a new LocationPicker
29333 * @param {Object} config The config object
29337 Roo.bootstrap.LocationPicker = function(config){
29339 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29344 * Fires when the picker initialized.
29345 * @param {Roo.bootstrap.LocationPicker} this
29346 * @param {Google Location} location
29350 * @event positionchanged
29351 * Fires when the picker position changed.
29352 * @param {Roo.bootstrap.LocationPicker} this
29353 * @param {Google Location} location
29355 positionchanged : true,
29358 * Fires when the map resize.
29359 * @param {Roo.bootstrap.LocationPicker} this
29364 * Fires when the map show.
29365 * @param {Roo.bootstrap.LocationPicker} this
29370 * Fires when the map hide.
29371 * @param {Roo.bootstrap.LocationPicker} this
29376 * Fires when click the map.
29377 * @param {Roo.bootstrap.LocationPicker} this
29378 * @param {Map event} e
29382 * @event mapRightClick
29383 * Fires when right click the map.
29384 * @param {Roo.bootstrap.LocationPicker} this
29385 * @param {Map event} e
29387 mapRightClick : true,
29389 * @event markerClick
29390 * Fires when click the marker.
29391 * @param {Roo.bootstrap.LocationPicker} this
29392 * @param {Map event} e
29394 markerClick : true,
29396 * @event markerRightClick
29397 * Fires when right click the marker.
29398 * @param {Roo.bootstrap.LocationPicker} this
29399 * @param {Map event} e
29401 markerRightClick : true,
29403 * @event OverlayViewDraw
29404 * Fires when OverlayView Draw
29405 * @param {Roo.bootstrap.LocationPicker} this
29407 OverlayViewDraw : true,
29409 * @event OverlayViewOnAdd
29410 * Fires when OverlayView Draw
29411 * @param {Roo.bootstrap.LocationPicker} this
29413 OverlayViewOnAdd : true,
29415 * @event OverlayViewOnRemove
29416 * Fires when OverlayView Draw
29417 * @param {Roo.bootstrap.LocationPicker} this
29419 OverlayViewOnRemove : true,
29421 * @event OverlayViewShow
29422 * Fires when OverlayView Draw
29423 * @param {Roo.bootstrap.LocationPicker} this
29424 * @param {Pixel} cpx
29426 OverlayViewShow : true,
29428 * @event OverlayViewHide
29429 * Fires when OverlayView Draw
29430 * @param {Roo.bootstrap.LocationPicker} this
29432 OverlayViewHide : true,
29434 * @event loadexception
29435 * Fires when load google lib failed.
29436 * @param {Roo.bootstrap.LocationPicker} this
29438 loadexception : true
29443 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29445 gMapContext: false,
29451 mapTypeControl: false,
29452 disableDoubleClickZoom: false,
29454 streetViewControl: false,
29458 enableAutocomplete: false,
29459 enableReverseGeocode: true,
29462 getAutoCreate: function()
29467 cls: 'roo-location-picker'
29473 initEvents: function(ct, position)
29475 if(!this.el.getWidth() || this.isApplied()){
29479 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29484 initial: function()
29486 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29487 this.fireEvent('loadexception', this);
29491 if(!this.mapTypeId){
29492 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29495 this.gMapContext = this.GMapContext();
29497 this.initOverlayView();
29499 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29503 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29504 _this.setPosition(_this.gMapContext.marker.position);
29507 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29508 _this.fireEvent('mapClick', this, event);
29512 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29513 _this.fireEvent('mapRightClick', this, event);
29517 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29518 _this.fireEvent('markerClick', this, event);
29522 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29523 _this.fireEvent('markerRightClick', this, event);
29527 this.setPosition(this.gMapContext.location);
29529 this.fireEvent('initial', this, this.gMapContext.location);
29532 initOverlayView: function()
29536 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29540 _this.fireEvent('OverlayViewDraw', _this);
29545 _this.fireEvent('OverlayViewOnAdd', _this);
29548 onRemove: function()
29550 _this.fireEvent('OverlayViewOnRemove', _this);
29553 show: function(cpx)
29555 _this.fireEvent('OverlayViewShow', _this, cpx);
29560 _this.fireEvent('OverlayViewHide', _this);
29566 fromLatLngToContainerPixel: function(event)
29568 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29571 isApplied: function()
29573 return this.getGmapContext() == false ? false : true;
29576 getGmapContext: function()
29578 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29581 GMapContext: function()
29583 var position = new google.maps.LatLng(this.latitude, this.longitude);
29585 var _map = new google.maps.Map(this.el.dom, {
29588 mapTypeId: this.mapTypeId,
29589 mapTypeControl: this.mapTypeControl,
29590 disableDoubleClickZoom: this.disableDoubleClickZoom,
29591 scrollwheel: this.scrollwheel,
29592 streetViewControl: this.streetViewControl,
29593 locationName: this.locationName,
29594 draggable: this.draggable,
29595 enableAutocomplete: this.enableAutocomplete,
29596 enableReverseGeocode: this.enableReverseGeocode
29599 var _marker = new google.maps.Marker({
29600 position: position,
29602 title: this.markerTitle,
29603 draggable: this.draggable
29610 location: position,
29611 radius: this.radius,
29612 locationName: this.locationName,
29613 addressComponents: {
29614 formatted_address: null,
29615 addressLine1: null,
29616 addressLine2: null,
29618 streetNumber: null,
29622 stateOrProvince: null
29625 domContainer: this.el.dom,
29626 geodecoder: new google.maps.Geocoder()
29630 drawCircle: function(center, radius, options)
29632 if (this.gMapContext.circle != null) {
29633 this.gMapContext.circle.setMap(null);
29637 options = Roo.apply({}, options, {
29638 strokeColor: "#0000FF",
29639 strokeOpacity: .35,
29641 fillColor: "#0000FF",
29645 options.map = this.gMapContext.map;
29646 options.radius = radius;
29647 options.center = center;
29648 this.gMapContext.circle = new google.maps.Circle(options);
29649 return this.gMapContext.circle;
29655 setPosition: function(location)
29657 this.gMapContext.location = location;
29658 this.gMapContext.marker.setPosition(location);
29659 this.gMapContext.map.panTo(location);
29660 this.drawCircle(location, this.gMapContext.radius, {});
29664 if (this.gMapContext.settings.enableReverseGeocode) {
29665 this.gMapContext.geodecoder.geocode({
29666 latLng: this.gMapContext.location
29667 }, function(results, status) {
29669 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29670 _this.gMapContext.locationName = results[0].formatted_address;
29671 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29673 _this.fireEvent('positionchanged', this, location);
29680 this.fireEvent('positionchanged', this, location);
29685 google.maps.event.trigger(this.gMapContext.map, "resize");
29687 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29689 this.fireEvent('resize', this);
29692 setPositionByLatLng: function(latitude, longitude)
29694 this.setPosition(new google.maps.LatLng(latitude, longitude));
29697 getCurrentPosition: function()
29700 latitude: this.gMapContext.location.lat(),
29701 longitude: this.gMapContext.location.lng()
29705 getAddressName: function()
29707 return this.gMapContext.locationName;
29710 getAddressComponents: function()
29712 return this.gMapContext.addressComponents;
29715 address_component_from_google_geocode: function(address_components)
29719 for (var i = 0; i < address_components.length; i++) {
29720 var component = address_components[i];
29721 if (component.types.indexOf("postal_code") >= 0) {
29722 result.postalCode = component.short_name;
29723 } else if (component.types.indexOf("street_number") >= 0) {
29724 result.streetNumber = component.short_name;
29725 } else if (component.types.indexOf("route") >= 0) {
29726 result.streetName = component.short_name;
29727 } else if (component.types.indexOf("neighborhood") >= 0) {
29728 result.city = component.short_name;
29729 } else if (component.types.indexOf("locality") >= 0) {
29730 result.city = component.short_name;
29731 } else if (component.types.indexOf("sublocality") >= 0) {
29732 result.district = component.short_name;
29733 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29734 result.stateOrProvince = component.short_name;
29735 } else if (component.types.indexOf("country") >= 0) {
29736 result.country = component.short_name;
29740 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29741 result.addressLine2 = "";
29745 setZoomLevel: function(zoom)
29747 this.gMapContext.map.setZoom(zoom);
29760 this.fireEvent('show', this);
29771 this.fireEvent('hide', this);
29776 Roo.apply(Roo.bootstrap.LocationPicker, {
29778 OverlayView : function(map, options)
29780 options = options || {};
29787 * @class Roo.bootstrap.Alert
29788 * @extends Roo.bootstrap.Component
29789 * Bootstrap Alert class - shows an alert area box
29791 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29792 Enter a valid email address
29795 * @cfg {String} title The title of alert
29796 * @cfg {String} html The content of alert
29797 * @cfg {String} weight ( success | info | warning | danger )
29798 * @cfg {String} faicon font-awesomeicon
29801 * Create a new alert
29802 * @param {Object} config The config object
29806 Roo.bootstrap.Alert = function(config){
29807 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29811 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29818 getAutoCreate : function()
29827 cls : 'roo-alert-icon'
29832 cls : 'roo-alert-title',
29837 cls : 'roo-alert-text',
29844 cfg.cn[0].cls += ' fa ' + this.faicon;
29848 cfg.cls += ' alert-' + this.weight;
29854 initEvents: function()
29856 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29859 setTitle : function(str)
29861 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29864 setText : function(str)
29866 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29869 setWeight : function(weight)
29872 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29875 this.weight = weight;
29877 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29880 setIcon : function(icon)
29883 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29886 this.faicon = icon;
29888 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29909 * @class Roo.bootstrap.UploadCropbox
29910 * @extends Roo.bootstrap.Component
29911 * Bootstrap UploadCropbox class
29912 * @cfg {String} emptyText show when image has been loaded
29913 * @cfg {String} rotateNotify show when image too small to rotate
29914 * @cfg {Number} errorTimeout default 3000
29915 * @cfg {Number} minWidth default 300
29916 * @cfg {Number} minHeight default 300
29917 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29918 * @cfg {Boolean} isDocument (true|false) default false
29919 * @cfg {String} url action url
29920 * @cfg {String} paramName default 'imageUpload'
29921 * @cfg {String} method default POST
29922 * @cfg {Boolean} loadMask (true|false) default true
29923 * @cfg {Boolean} loadingText default 'Loading...'
29926 * Create a new UploadCropbox
29927 * @param {Object} config The config object
29930 Roo.bootstrap.UploadCropbox = function(config){
29931 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29935 * @event beforeselectfile
29936 * Fire before select file
29937 * @param {Roo.bootstrap.UploadCropbox} this
29939 "beforeselectfile" : true,
29942 * Fire after initEvent
29943 * @param {Roo.bootstrap.UploadCropbox} this
29948 * Fire after initEvent
29949 * @param {Roo.bootstrap.UploadCropbox} this
29950 * @param {String} data
29955 * Fire when preparing the file data
29956 * @param {Roo.bootstrap.UploadCropbox} this
29957 * @param {Object} file
29962 * Fire when get exception
29963 * @param {Roo.bootstrap.UploadCropbox} this
29964 * @param {XMLHttpRequest} xhr
29966 "exception" : true,
29968 * @event beforeloadcanvas
29969 * Fire before load the canvas
29970 * @param {Roo.bootstrap.UploadCropbox} this
29971 * @param {String} src
29973 "beforeloadcanvas" : true,
29976 * Fire when trash image
29977 * @param {Roo.bootstrap.UploadCropbox} this
29982 * Fire when download the image
29983 * @param {Roo.bootstrap.UploadCropbox} this
29987 * @event footerbuttonclick
29988 * Fire when footerbuttonclick
29989 * @param {Roo.bootstrap.UploadCropbox} this
29990 * @param {String} type
29992 "footerbuttonclick" : true,
29996 * @param {Roo.bootstrap.UploadCropbox} this
30001 * Fire when rotate the image
30002 * @param {Roo.bootstrap.UploadCropbox} this
30003 * @param {String} pos
30008 * Fire when inspect the file
30009 * @param {Roo.bootstrap.UploadCropbox} this
30010 * @param {Object} file
30015 * Fire when xhr upload the file
30016 * @param {Roo.bootstrap.UploadCropbox} this
30017 * @param {Object} data
30022 * Fire when arrange the file data
30023 * @param {Roo.bootstrap.UploadCropbox} this
30024 * @param {Object} formData
30029 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30032 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30034 emptyText : 'Click to upload image',
30035 rotateNotify : 'Image is too small to rotate',
30036 errorTimeout : 3000,
30050 cropType : 'image/jpeg',
30052 canvasLoaded : false,
30053 isDocument : false,
30055 paramName : 'imageUpload',
30057 loadingText : 'Loading...',
30060 getAutoCreate : function()
30064 cls : 'roo-upload-cropbox',
30068 cls : 'roo-upload-cropbox-selector',
30073 cls : 'roo-upload-cropbox-body',
30074 style : 'cursor:pointer',
30078 cls : 'roo-upload-cropbox-preview'
30082 cls : 'roo-upload-cropbox-thumb'
30086 cls : 'roo-upload-cropbox-empty-notify',
30087 html : this.emptyText
30091 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30092 html : this.rotateNotify
30098 cls : 'roo-upload-cropbox-footer',
30101 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30111 onRender : function(ct, position)
30113 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30115 if (this.buttons.length) {
30117 Roo.each(this.buttons, function(bb) {
30119 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30121 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30127 this.maskEl = this.el;
30131 initEvents : function()
30133 this.urlAPI = (window.createObjectURL && window) ||
30134 (window.URL && URL.revokeObjectURL && URL) ||
30135 (window.webkitURL && webkitURL);
30137 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30138 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30140 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30141 this.selectorEl.hide();
30143 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30144 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30146 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30147 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30148 this.thumbEl.hide();
30150 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30151 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30153 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30154 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30155 this.errorEl.hide();
30157 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30158 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30159 this.footerEl.hide();
30161 this.setThumbBoxSize();
30167 this.fireEvent('initial', this);
30174 window.addEventListener("resize", function() { _this.resize(); } );
30176 this.bodyEl.on('click', this.beforeSelectFile, this);
30179 this.bodyEl.on('touchstart', this.onTouchStart, this);
30180 this.bodyEl.on('touchmove', this.onTouchMove, this);
30181 this.bodyEl.on('touchend', this.onTouchEnd, this);
30185 this.bodyEl.on('mousedown', this.onMouseDown, this);
30186 this.bodyEl.on('mousemove', this.onMouseMove, this);
30187 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30188 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30189 Roo.get(document).on('mouseup', this.onMouseUp, this);
30192 this.selectorEl.on('change', this.onFileSelected, this);
30198 this.baseScale = 1;
30200 this.baseRotate = 1;
30201 this.dragable = false;
30202 this.pinching = false;
30205 this.cropData = false;
30206 this.notifyEl.dom.innerHTML = this.emptyText;
30208 this.selectorEl.dom.value = '';
30212 resize : function()
30214 if(this.fireEvent('resize', this) != false){
30215 this.setThumbBoxPosition();
30216 this.setCanvasPosition();
30220 onFooterButtonClick : function(e, el, o, type)
30223 case 'rotate-left' :
30224 this.onRotateLeft(e);
30226 case 'rotate-right' :
30227 this.onRotateRight(e);
30230 this.beforeSelectFile(e);
30245 this.fireEvent('footerbuttonclick', this, type);
30248 beforeSelectFile : function(e)
30250 e.preventDefault();
30252 if(this.fireEvent('beforeselectfile', this) != false){
30253 this.selectorEl.dom.click();
30257 onFileSelected : function(e)
30259 e.preventDefault();
30261 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30265 var file = this.selectorEl.dom.files[0];
30267 if(this.fireEvent('inspect', this, file) != false){
30268 this.prepare(file);
30273 trash : function(e)
30275 this.fireEvent('trash', this);
30278 download : function(e)
30280 this.fireEvent('download', this);
30283 loadCanvas : function(src)
30285 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30289 this.imageEl = document.createElement('img');
30293 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30295 this.imageEl.src = src;
30299 onLoadCanvas : function()
30301 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30302 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30304 this.bodyEl.un('click', this.beforeSelectFile, this);
30306 this.notifyEl.hide();
30307 this.thumbEl.show();
30308 this.footerEl.show();
30310 this.baseRotateLevel();
30312 if(this.isDocument){
30313 this.setThumbBoxSize();
30316 this.setThumbBoxPosition();
30318 this.baseScaleLevel();
30324 this.canvasLoaded = true;
30327 this.maskEl.unmask();
30332 setCanvasPosition : function()
30334 if(!this.canvasEl){
30338 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30339 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30341 this.previewEl.setLeft(pw);
30342 this.previewEl.setTop(ph);
30346 onMouseDown : function(e)
30350 this.dragable = true;
30351 this.pinching = false;
30353 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30354 this.dragable = false;
30358 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30359 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30363 onMouseMove : function(e)
30367 if(!this.canvasLoaded){
30371 if (!this.dragable){
30375 var minX = Math.ceil(this.thumbEl.getLeft(true));
30376 var minY = Math.ceil(this.thumbEl.getTop(true));
30378 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30379 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30381 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30382 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30384 x = x - this.mouseX;
30385 y = y - this.mouseY;
30387 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30388 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30390 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30391 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30393 this.previewEl.setLeft(bgX);
30394 this.previewEl.setTop(bgY);
30396 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30397 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30400 onMouseUp : function(e)
30404 this.dragable = false;
30407 onMouseWheel : function(e)
30411 this.startScale = this.scale;
30413 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30415 if(!this.zoomable()){
30416 this.scale = this.startScale;
30425 zoomable : function()
30427 var minScale = this.thumbEl.getWidth() / this.minWidth;
30429 if(this.minWidth < this.minHeight){
30430 minScale = this.thumbEl.getHeight() / this.minHeight;
30433 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30434 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30438 (this.rotate == 0 || this.rotate == 180) &&
30440 width > this.imageEl.OriginWidth ||
30441 height > this.imageEl.OriginHeight ||
30442 (width < this.minWidth && height < this.minHeight)
30450 (this.rotate == 90 || this.rotate == 270) &&
30452 width > this.imageEl.OriginWidth ||
30453 height > this.imageEl.OriginHeight ||
30454 (width < this.minHeight && height < this.minWidth)
30461 !this.isDocument &&
30462 (this.rotate == 0 || this.rotate == 180) &&
30464 width < this.minWidth ||
30465 width > this.imageEl.OriginWidth ||
30466 height < this.minHeight ||
30467 height > this.imageEl.OriginHeight
30474 !this.isDocument &&
30475 (this.rotate == 90 || this.rotate == 270) &&
30477 width < this.minHeight ||
30478 width > this.imageEl.OriginWidth ||
30479 height < this.minWidth ||
30480 height > this.imageEl.OriginHeight
30490 onRotateLeft : function(e)
30492 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30494 var minScale = this.thumbEl.getWidth() / this.minWidth;
30496 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30497 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30499 this.startScale = this.scale;
30501 while (this.getScaleLevel() < minScale){
30503 this.scale = this.scale + 1;
30505 if(!this.zoomable()){
30510 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30511 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30516 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30523 this.scale = this.startScale;
30525 this.onRotateFail();
30530 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30532 if(this.isDocument){
30533 this.setThumbBoxSize();
30534 this.setThumbBoxPosition();
30535 this.setCanvasPosition();
30540 this.fireEvent('rotate', this, 'left');
30544 onRotateRight : function(e)
30546 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30548 var minScale = this.thumbEl.getWidth() / this.minWidth;
30550 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30551 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30553 this.startScale = this.scale;
30555 while (this.getScaleLevel() < minScale){
30557 this.scale = this.scale + 1;
30559 if(!this.zoomable()){
30564 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30565 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30570 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30577 this.scale = this.startScale;
30579 this.onRotateFail();
30584 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30586 if(this.isDocument){
30587 this.setThumbBoxSize();
30588 this.setThumbBoxPosition();
30589 this.setCanvasPosition();
30594 this.fireEvent('rotate', this, 'right');
30597 onRotateFail : function()
30599 this.errorEl.show(true);
30603 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30608 this.previewEl.dom.innerHTML = '';
30610 var canvasEl = document.createElement("canvas");
30612 var contextEl = canvasEl.getContext("2d");
30614 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30615 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30616 var center = this.imageEl.OriginWidth / 2;
30618 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30619 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30620 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30621 center = this.imageEl.OriginHeight / 2;
30624 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30626 contextEl.translate(center, center);
30627 contextEl.rotate(this.rotate * Math.PI / 180);
30629 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30631 this.canvasEl = document.createElement("canvas");
30633 this.contextEl = this.canvasEl.getContext("2d");
30635 switch (this.rotate) {
30638 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30639 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30641 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30646 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30647 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30649 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30650 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);
30654 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30659 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30660 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30662 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30663 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);
30667 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);
30672 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30673 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30675 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30676 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30680 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);
30687 this.previewEl.appendChild(this.canvasEl);
30689 this.setCanvasPosition();
30694 if(!this.canvasLoaded){
30698 var imageCanvas = document.createElement("canvas");
30700 var imageContext = imageCanvas.getContext("2d");
30702 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30703 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30705 var center = imageCanvas.width / 2;
30707 imageContext.translate(center, center);
30709 imageContext.rotate(this.rotate * Math.PI / 180);
30711 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30713 var canvas = document.createElement("canvas");
30715 var context = canvas.getContext("2d");
30717 canvas.width = this.minWidth;
30718 canvas.height = this.minHeight;
30720 switch (this.rotate) {
30723 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30724 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30726 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30727 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30729 var targetWidth = this.minWidth - 2 * x;
30730 var targetHeight = this.minHeight - 2 * y;
30734 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30735 scale = targetWidth / width;
30738 if(x > 0 && y == 0){
30739 scale = targetHeight / height;
30742 if(x > 0 && y > 0){
30743 scale = targetWidth / width;
30745 if(width < height){
30746 scale = targetHeight / height;
30750 context.scale(scale, scale);
30752 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30753 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30755 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30756 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30758 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30763 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30764 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30766 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30767 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30769 var targetWidth = this.minWidth - 2 * x;
30770 var targetHeight = this.minHeight - 2 * y;
30774 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30775 scale = targetWidth / width;
30778 if(x > 0 && y == 0){
30779 scale = targetHeight / height;
30782 if(x > 0 && y > 0){
30783 scale = targetWidth / width;
30785 if(width < height){
30786 scale = targetHeight / height;
30790 context.scale(scale, scale);
30792 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30793 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30795 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30796 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30798 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30800 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30805 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30806 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30808 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30809 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30811 var targetWidth = this.minWidth - 2 * x;
30812 var targetHeight = this.minHeight - 2 * y;
30816 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30817 scale = targetWidth / width;
30820 if(x > 0 && y == 0){
30821 scale = targetHeight / height;
30824 if(x > 0 && y > 0){
30825 scale = targetWidth / width;
30827 if(width < height){
30828 scale = targetHeight / height;
30832 context.scale(scale, scale);
30834 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30835 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30837 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30838 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30840 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30841 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30843 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30848 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30849 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30851 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30852 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30854 var targetWidth = this.minWidth - 2 * x;
30855 var targetHeight = this.minHeight - 2 * y;
30859 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30860 scale = targetWidth / width;
30863 if(x > 0 && y == 0){
30864 scale = targetHeight / height;
30867 if(x > 0 && y > 0){
30868 scale = targetWidth / width;
30870 if(width < height){
30871 scale = targetHeight / height;
30875 context.scale(scale, scale);
30877 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30878 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30880 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30881 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30883 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30885 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30892 this.cropData = canvas.toDataURL(this.cropType);
30894 if(this.fireEvent('crop', this, this.cropData) !== false){
30895 this.process(this.file, this.cropData);
30902 setThumbBoxSize : function()
30906 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30907 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30908 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30910 this.minWidth = width;
30911 this.minHeight = height;
30913 if(this.rotate == 90 || this.rotate == 270){
30914 this.minWidth = height;
30915 this.minHeight = width;
30920 width = Math.ceil(this.minWidth * height / this.minHeight);
30922 if(this.minWidth > this.minHeight){
30924 height = Math.ceil(this.minHeight * width / this.minWidth);
30927 this.thumbEl.setStyle({
30928 width : width + 'px',
30929 height : height + 'px'
30936 setThumbBoxPosition : function()
30938 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30939 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30941 this.thumbEl.setLeft(x);
30942 this.thumbEl.setTop(y);
30946 baseRotateLevel : function()
30948 this.baseRotate = 1;
30951 typeof(this.exif) != 'undefined' &&
30952 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30953 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30955 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30958 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30962 baseScaleLevel : function()
30966 if(this.isDocument){
30968 if(this.baseRotate == 6 || this.baseRotate == 8){
30970 height = this.thumbEl.getHeight();
30971 this.baseScale = height / this.imageEl.OriginWidth;
30973 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30974 width = this.thumbEl.getWidth();
30975 this.baseScale = width / this.imageEl.OriginHeight;
30981 height = this.thumbEl.getHeight();
30982 this.baseScale = height / this.imageEl.OriginHeight;
30984 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30985 width = this.thumbEl.getWidth();
30986 this.baseScale = width / this.imageEl.OriginWidth;
30992 if(this.baseRotate == 6 || this.baseRotate == 8){
30994 width = this.thumbEl.getHeight();
30995 this.baseScale = width / this.imageEl.OriginHeight;
30997 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30998 height = this.thumbEl.getWidth();
30999 this.baseScale = height / this.imageEl.OriginHeight;
31002 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31003 height = this.thumbEl.getWidth();
31004 this.baseScale = height / this.imageEl.OriginHeight;
31006 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31007 width = this.thumbEl.getHeight();
31008 this.baseScale = width / this.imageEl.OriginWidth;
31015 width = this.thumbEl.getWidth();
31016 this.baseScale = width / this.imageEl.OriginWidth;
31018 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31019 height = this.thumbEl.getHeight();
31020 this.baseScale = height / this.imageEl.OriginHeight;
31023 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31025 height = this.thumbEl.getHeight();
31026 this.baseScale = height / this.imageEl.OriginHeight;
31028 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31029 width = this.thumbEl.getWidth();
31030 this.baseScale = width / this.imageEl.OriginWidth;
31038 getScaleLevel : function()
31040 return this.baseScale * Math.pow(1.1, this.scale);
31043 onTouchStart : function(e)
31045 if(!this.canvasLoaded){
31046 this.beforeSelectFile(e);
31050 var touches = e.browserEvent.touches;
31056 if(touches.length == 1){
31057 this.onMouseDown(e);
31061 if(touches.length != 2){
31067 for(var i = 0, finger; finger = touches[i]; i++){
31068 coords.push(finger.pageX, finger.pageY);
31071 var x = Math.pow(coords[0] - coords[2], 2);
31072 var y = Math.pow(coords[1] - coords[3], 2);
31074 this.startDistance = Math.sqrt(x + y);
31076 this.startScale = this.scale;
31078 this.pinching = true;
31079 this.dragable = false;
31083 onTouchMove : function(e)
31085 if(!this.pinching && !this.dragable){
31089 var touches = e.browserEvent.touches;
31096 this.onMouseMove(e);
31102 for(var i = 0, finger; finger = touches[i]; i++){
31103 coords.push(finger.pageX, finger.pageY);
31106 var x = Math.pow(coords[0] - coords[2], 2);
31107 var y = Math.pow(coords[1] - coords[3], 2);
31109 this.endDistance = Math.sqrt(x + y);
31111 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31113 if(!this.zoomable()){
31114 this.scale = this.startScale;
31122 onTouchEnd : function(e)
31124 this.pinching = false;
31125 this.dragable = false;
31129 process : function(file, crop)
31132 this.maskEl.mask(this.loadingText);
31135 this.xhr = new XMLHttpRequest();
31137 file.xhr = this.xhr;
31139 this.xhr.open(this.method, this.url, true);
31142 "Accept": "application/json",
31143 "Cache-Control": "no-cache",
31144 "X-Requested-With": "XMLHttpRequest"
31147 for (var headerName in headers) {
31148 var headerValue = headers[headerName];
31150 this.xhr.setRequestHeader(headerName, headerValue);
31156 this.xhr.onload = function()
31158 _this.xhrOnLoad(_this.xhr);
31161 this.xhr.onerror = function()
31163 _this.xhrOnError(_this.xhr);
31166 var formData = new FormData();
31168 formData.append('returnHTML', 'NO');
31171 formData.append('crop', crop);
31174 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31175 formData.append(this.paramName, file, file.name);
31178 if(typeof(file.filename) != 'undefined'){
31179 formData.append('filename', file.filename);
31182 if(typeof(file.mimetype) != 'undefined'){
31183 formData.append('mimetype', file.mimetype);
31186 if(this.fireEvent('arrange', this, formData) != false){
31187 this.xhr.send(formData);
31191 xhrOnLoad : function(xhr)
31194 this.maskEl.unmask();
31197 if (xhr.readyState !== 4) {
31198 this.fireEvent('exception', this, xhr);
31202 var response = Roo.decode(xhr.responseText);
31204 if(!response.success){
31205 this.fireEvent('exception', this, xhr);
31209 var response = Roo.decode(xhr.responseText);
31211 this.fireEvent('upload', this, response);
31215 xhrOnError : function()
31218 this.maskEl.unmask();
31221 Roo.log('xhr on error');
31223 var response = Roo.decode(xhr.responseText);
31229 prepare : function(file)
31232 this.maskEl.mask(this.loadingText);
31238 if(typeof(file) === 'string'){
31239 this.loadCanvas(file);
31243 if(!file || !this.urlAPI){
31248 this.cropType = file.type;
31252 if(this.fireEvent('prepare', this, this.file) != false){
31254 var reader = new FileReader();
31256 reader.onload = function (e) {
31257 if (e.target.error) {
31258 Roo.log(e.target.error);
31262 var buffer = e.target.result,
31263 dataView = new DataView(buffer),
31265 maxOffset = dataView.byteLength - 4,
31269 if (dataView.getUint16(0) === 0xffd8) {
31270 while (offset < maxOffset) {
31271 markerBytes = dataView.getUint16(offset);
31273 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31274 markerLength = dataView.getUint16(offset + 2) + 2;
31275 if (offset + markerLength > dataView.byteLength) {
31276 Roo.log('Invalid meta data: Invalid segment size.');
31280 if(markerBytes == 0xffe1){
31281 _this.parseExifData(
31288 offset += markerLength;
31298 var url = _this.urlAPI.createObjectURL(_this.file);
31300 _this.loadCanvas(url);
31305 reader.readAsArrayBuffer(this.file);
31311 parseExifData : function(dataView, offset, length)
31313 var tiffOffset = offset + 10,
31317 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31318 // No Exif data, might be XMP data instead
31322 // Check for the ASCII code for "Exif" (0x45786966):
31323 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31324 // No Exif data, might be XMP data instead
31327 if (tiffOffset + 8 > dataView.byteLength) {
31328 Roo.log('Invalid Exif data: Invalid segment size.');
31331 // Check for the two null bytes:
31332 if (dataView.getUint16(offset + 8) !== 0x0000) {
31333 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31336 // Check the byte alignment:
31337 switch (dataView.getUint16(tiffOffset)) {
31339 littleEndian = true;
31342 littleEndian = false;
31345 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31348 // Check for the TIFF tag marker (0x002A):
31349 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31350 Roo.log('Invalid Exif data: Missing TIFF marker.');
31353 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31354 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31356 this.parseExifTags(
31359 tiffOffset + dirOffset,
31364 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31369 if (dirOffset + 6 > dataView.byteLength) {
31370 Roo.log('Invalid Exif data: Invalid directory offset.');
31373 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31374 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31375 if (dirEndOffset + 4 > dataView.byteLength) {
31376 Roo.log('Invalid Exif data: Invalid directory size.');
31379 for (i = 0; i < tagsNumber; i += 1) {
31383 dirOffset + 2 + 12 * i, // tag offset
31387 // Return the offset to the next directory:
31388 return dataView.getUint32(dirEndOffset, littleEndian);
31391 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31393 var tag = dataView.getUint16(offset, littleEndian);
31395 this.exif[tag] = this.getExifValue(
31399 dataView.getUint16(offset + 2, littleEndian), // tag type
31400 dataView.getUint32(offset + 4, littleEndian), // tag length
31405 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31407 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31416 Roo.log('Invalid Exif data: Invalid tag type.');
31420 tagSize = tagType.size * length;
31421 // Determine if the value is contained in the dataOffset bytes,
31422 // or if the value at the dataOffset is a pointer to the actual data:
31423 dataOffset = tagSize > 4 ?
31424 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31425 if (dataOffset + tagSize > dataView.byteLength) {
31426 Roo.log('Invalid Exif data: Invalid data offset.');
31429 if (length === 1) {
31430 return tagType.getValue(dataView, dataOffset, littleEndian);
31433 for (i = 0; i < length; i += 1) {
31434 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31437 if (tagType.ascii) {
31439 // Concatenate the chars:
31440 for (i = 0; i < values.length; i += 1) {
31442 // Ignore the terminating NULL byte(s):
31443 if (c === '\u0000') {
31455 Roo.apply(Roo.bootstrap.UploadCropbox, {
31457 'Orientation': 0x0112
31461 1: 0, //'top-left',
31463 3: 180, //'bottom-right',
31464 // 4: 'bottom-left',
31466 6: 90, //'right-top',
31467 // 7: 'right-bottom',
31468 8: 270 //'left-bottom'
31472 // byte, 8-bit unsigned int:
31474 getValue: function (dataView, dataOffset) {
31475 return dataView.getUint8(dataOffset);
31479 // ascii, 8-bit byte:
31481 getValue: function (dataView, dataOffset) {
31482 return String.fromCharCode(dataView.getUint8(dataOffset));
31487 // short, 16 bit int:
31489 getValue: function (dataView, dataOffset, littleEndian) {
31490 return dataView.getUint16(dataOffset, littleEndian);
31494 // long, 32 bit int:
31496 getValue: function (dataView, dataOffset, littleEndian) {
31497 return dataView.getUint32(dataOffset, littleEndian);
31501 // rational = two long values, first is numerator, second is denominator:
31503 getValue: function (dataView, dataOffset, littleEndian) {
31504 return dataView.getUint32(dataOffset, littleEndian) /
31505 dataView.getUint32(dataOffset + 4, littleEndian);
31509 // slong, 32 bit signed int:
31511 getValue: function (dataView, dataOffset, littleEndian) {
31512 return dataView.getInt32(dataOffset, littleEndian);
31516 // srational, two slongs, first is numerator, second is denominator:
31518 getValue: function (dataView, dataOffset, littleEndian) {
31519 return dataView.getInt32(dataOffset, littleEndian) /
31520 dataView.getInt32(dataOffset + 4, littleEndian);
31530 cls : 'btn-group roo-upload-cropbox-rotate-left',
31531 action : 'rotate-left',
31535 cls : 'btn btn-default',
31536 html : '<i class="fa fa-undo"></i>'
31542 cls : 'btn-group roo-upload-cropbox-picture',
31543 action : 'picture',
31547 cls : 'btn btn-default',
31548 html : '<i class="fa fa-picture-o"></i>'
31554 cls : 'btn-group roo-upload-cropbox-rotate-right',
31555 action : 'rotate-right',
31559 cls : 'btn btn-default',
31560 html : '<i class="fa fa-repeat"></i>'
31568 cls : 'btn-group roo-upload-cropbox-rotate-left',
31569 action : 'rotate-left',
31573 cls : 'btn btn-default',
31574 html : '<i class="fa fa-undo"></i>'
31580 cls : 'btn-group roo-upload-cropbox-download',
31581 action : 'download',
31585 cls : 'btn btn-default',
31586 html : '<i class="fa fa-download"></i>'
31592 cls : 'btn-group roo-upload-cropbox-crop',
31597 cls : 'btn btn-default',
31598 html : '<i class="fa fa-crop"></i>'
31604 cls : 'btn-group roo-upload-cropbox-trash',
31609 cls : 'btn btn-default',
31610 html : '<i class="fa fa-trash"></i>'
31616 cls : 'btn-group roo-upload-cropbox-rotate-right',
31617 action : 'rotate-right',
31621 cls : 'btn btn-default',
31622 html : '<i class="fa fa-repeat"></i>'
31630 cls : 'btn-group roo-upload-cropbox-rotate-left',
31631 action : 'rotate-left',
31635 cls : 'btn btn-default',
31636 html : '<i class="fa fa-undo"></i>'
31642 cls : 'btn-group roo-upload-cropbox-rotate-right',
31643 action : 'rotate-right',
31647 cls : 'btn btn-default',
31648 html : '<i class="fa fa-repeat"></i>'
31661 * @class Roo.bootstrap.DocumentManager
31662 * @extends Roo.bootstrap.Component
31663 * Bootstrap DocumentManager class
31664 * @cfg {String} paramName default 'imageUpload'
31665 * @cfg {String} toolTipName default 'filename'
31666 * @cfg {String} method default POST
31667 * @cfg {String} url action url
31668 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31669 * @cfg {Boolean} multiple multiple upload default true
31670 * @cfg {Number} thumbSize default 300
31671 * @cfg {String} fieldLabel
31672 * @cfg {Number} labelWidth default 4
31673 * @cfg {String} labelAlign (left|top) default left
31674 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31675 * @cfg {Number} labellg set the width of label (1-12)
31676 * @cfg {Number} labelmd set the width of label (1-12)
31677 * @cfg {Number} labelsm set the width of label (1-12)
31678 * @cfg {Number} labelxs set the width of label (1-12)
31681 * Create a new DocumentManager
31682 * @param {Object} config The config object
31685 Roo.bootstrap.DocumentManager = function(config){
31686 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31689 this.delegates = [];
31694 * Fire when initial the DocumentManager
31695 * @param {Roo.bootstrap.DocumentManager} this
31700 * inspect selected file
31701 * @param {Roo.bootstrap.DocumentManager} this
31702 * @param {File} file
31707 * Fire when xhr load exception
31708 * @param {Roo.bootstrap.DocumentManager} this
31709 * @param {XMLHttpRequest} xhr
31711 "exception" : true,
31713 * @event afterupload
31714 * Fire when xhr load exception
31715 * @param {Roo.bootstrap.DocumentManager} this
31716 * @param {XMLHttpRequest} xhr
31718 "afterupload" : true,
31721 * prepare the form data
31722 * @param {Roo.bootstrap.DocumentManager} this
31723 * @param {Object} formData
31728 * Fire when remove the file
31729 * @param {Roo.bootstrap.DocumentManager} this
31730 * @param {Object} file
31735 * Fire after refresh the file
31736 * @param {Roo.bootstrap.DocumentManager} this
31741 * Fire after click the image
31742 * @param {Roo.bootstrap.DocumentManager} this
31743 * @param {Object} file
31748 * Fire when upload a image and editable set to true
31749 * @param {Roo.bootstrap.DocumentManager} this
31750 * @param {Object} file
31754 * @event beforeselectfile
31755 * Fire before select file
31756 * @param {Roo.bootstrap.DocumentManager} this
31758 "beforeselectfile" : true,
31761 * Fire before process file
31762 * @param {Roo.bootstrap.DocumentManager} this
31763 * @param {Object} file
31767 * @event previewrendered
31768 * Fire when preview rendered
31769 * @param {Roo.bootstrap.DocumentManager} this
31770 * @param {Object} file
31772 "previewrendered" : true,
31775 "previewResize" : true
31780 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31789 paramName : 'imageUpload',
31790 toolTipName : 'filename',
31793 labelAlign : 'left',
31803 getAutoCreate : function()
31805 var managerWidget = {
31807 cls : 'roo-document-manager',
31811 cls : 'roo-document-manager-selector',
31816 cls : 'roo-document-manager-uploader',
31820 cls : 'roo-document-manager-upload-btn',
31821 html : '<i class="fa fa-plus"></i>'
31832 cls : 'column col-md-12',
31837 if(this.fieldLabel.length){
31842 cls : 'column col-md-12',
31843 html : this.fieldLabel
31847 cls : 'column col-md-12',
31852 if(this.labelAlign == 'left'){
31857 html : this.fieldLabel
31866 if(this.labelWidth > 12){
31867 content[0].style = "width: " + this.labelWidth + 'px';
31870 if(this.labelWidth < 13 && this.labelmd == 0){
31871 this.labelmd = this.labelWidth;
31874 if(this.labellg > 0){
31875 content[0].cls += ' col-lg-' + this.labellg;
31876 content[1].cls += ' col-lg-' + (12 - this.labellg);
31879 if(this.labelmd > 0){
31880 content[0].cls += ' col-md-' + this.labelmd;
31881 content[1].cls += ' col-md-' + (12 - this.labelmd);
31884 if(this.labelsm > 0){
31885 content[0].cls += ' col-sm-' + this.labelsm;
31886 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31889 if(this.labelxs > 0){
31890 content[0].cls += ' col-xs-' + this.labelxs;
31891 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31899 cls : 'row clearfix',
31907 initEvents : function()
31909 this.managerEl = this.el.select('.roo-document-manager', true).first();
31910 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31912 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31913 this.selectorEl.hide();
31916 this.selectorEl.attr('multiple', 'multiple');
31919 this.selectorEl.on('change', this.onFileSelected, this);
31921 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31922 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31924 this.uploader.on('click', this.onUploaderClick, this);
31926 this.renderProgressDialog();
31930 window.addEventListener("resize", function() { _this.refresh(); } );
31932 this.fireEvent('initial', this);
31935 renderProgressDialog : function()
31939 this.progressDialog = new Roo.bootstrap.Modal({
31940 cls : 'roo-document-manager-progress-dialog',
31941 allow_close : false,
31952 btnclick : function() {
31953 _this.uploadCancel();
31959 this.progressDialog.render(Roo.get(document.body));
31961 this.progress = new Roo.bootstrap.Progress({
31962 cls : 'roo-document-manager-progress',
31967 this.progress.render(this.progressDialog.getChildContainer());
31969 this.progressBar = new Roo.bootstrap.ProgressBar({
31970 cls : 'roo-document-manager-progress-bar',
31973 aria_valuemax : 12,
31977 this.progressBar.render(this.progress.getChildContainer());
31980 onUploaderClick : function(e)
31982 e.preventDefault();
31984 if(this.fireEvent('beforeselectfile', this) != false){
31985 this.selectorEl.dom.click();
31990 onFileSelected : function(e)
31992 e.preventDefault();
31994 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31998 Roo.each(this.selectorEl.dom.files, function(file){
31999 if(this.fireEvent('inspect', this, file) != false){
32000 this.files.push(file);
32010 this.selectorEl.dom.value = '';
32012 if(!this.files || !this.files.length){
32016 if(this.boxes > 0 && this.files.length > this.boxes){
32017 this.files = this.files.slice(0, this.boxes);
32020 this.uploader.show();
32022 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32023 this.uploader.hide();
32032 Roo.each(this.files, function(file){
32034 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32035 var f = this.renderPreview(file);
32040 if(file.type.indexOf('image') != -1){
32041 this.delegates.push(
32043 _this.process(file);
32044 }).createDelegate(this)
32052 _this.process(file);
32053 }).createDelegate(this)
32058 this.files = files;
32060 this.delegates = this.delegates.concat(docs);
32062 if(!this.delegates.length){
32067 this.progressBar.aria_valuemax = this.delegates.length;
32074 arrange : function()
32076 if(!this.delegates.length){
32077 this.progressDialog.hide();
32082 var delegate = this.delegates.shift();
32084 this.progressDialog.show();
32086 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32088 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32093 refresh : function()
32095 this.uploader.show();
32097 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32098 this.uploader.hide();
32101 Roo.isTouch ? this.closable(false) : this.closable(true);
32103 this.fireEvent('refresh', this);
32106 onRemove : function(e, el, o)
32108 e.preventDefault();
32110 this.fireEvent('remove', this, o);
32114 remove : function(o)
32118 Roo.each(this.files, function(file){
32119 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32128 this.files = files;
32135 Roo.each(this.files, function(file){
32140 file.target.remove();
32149 onClick : function(e, el, o)
32151 e.preventDefault();
32153 this.fireEvent('click', this, o);
32157 closable : function(closable)
32159 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32161 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32173 xhrOnLoad : function(xhr)
32175 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32179 if (xhr.readyState !== 4) {
32181 this.fireEvent('exception', this, xhr);
32185 var response = Roo.decode(xhr.responseText);
32187 if(!response.success){
32189 this.fireEvent('exception', this, xhr);
32193 var file = this.renderPreview(response.data);
32195 this.files.push(file);
32199 this.fireEvent('afterupload', this, xhr);
32203 xhrOnError : function(xhr)
32205 Roo.log('xhr on error');
32207 var response = Roo.decode(xhr.responseText);
32214 process : function(file)
32216 if(this.fireEvent('process', this, file) !== false){
32217 if(this.editable && file.type.indexOf('image') != -1){
32218 this.fireEvent('edit', this, file);
32222 this.uploadStart(file, false);
32229 uploadStart : function(file, crop)
32231 this.xhr = new XMLHttpRequest();
32233 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32238 file.xhr = this.xhr;
32240 this.managerEl.createChild({
32242 cls : 'roo-document-manager-loading',
32246 tooltip : file.name,
32247 cls : 'roo-document-manager-thumb',
32248 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32254 this.xhr.open(this.method, this.url, true);
32257 "Accept": "application/json",
32258 "Cache-Control": "no-cache",
32259 "X-Requested-With": "XMLHttpRequest"
32262 for (var headerName in headers) {
32263 var headerValue = headers[headerName];
32265 this.xhr.setRequestHeader(headerName, headerValue);
32271 this.xhr.onload = function()
32273 _this.xhrOnLoad(_this.xhr);
32276 this.xhr.onerror = function()
32278 _this.xhrOnError(_this.xhr);
32281 var formData = new FormData();
32283 formData.append('returnHTML', 'NO');
32286 formData.append('crop', crop);
32289 formData.append(this.paramName, file, file.name);
32296 if(this.fireEvent('prepare', this, formData, options) != false){
32298 if(options.manually){
32302 this.xhr.send(formData);
32306 this.uploadCancel();
32309 uploadCancel : function()
32315 this.delegates = [];
32317 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32324 renderPreview : function(file)
32326 if(typeof(file.target) != 'undefined' && file.target){
32330 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32332 var previewEl = this.managerEl.createChild({
32334 cls : 'roo-document-manager-preview',
32338 tooltip : file[this.toolTipName],
32339 cls : 'roo-document-manager-thumb',
32340 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32345 html : '<i class="fa fa-times-circle"></i>'
32350 var close = previewEl.select('button.close', true).first();
32352 close.on('click', this.onRemove, this, file);
32354 file.target = previewEl;
32356 var image = previewEl.select('img', true).first();
32360 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32362 image.on('click', this.onClick, this, file);
32364 this.fireEvent('previewrendered', this, file);
32370 onPreviewLoad : function(file, image)
32372 if(typeof(file.target) == 'undefined' || !file.target){
32376 var width = image.dom.naturalWidth || image.dom.width;
32377 var height = image.dom.naturalHeight || image.dom.height;
32379 if(!this.previewResize) {
32383 if(width > height){
32384 file.target.addClass('wide');
32388 file.target.addClass('tall');
32393 uploadFromSource : function(file, crop)
32395 this.xhr = new XMLHttpRequest();
32397 this.managerEl.createChild({
32399 cls : 'roo-document-manager-loading',
32403 tooltip : file.name,
32404 cls : 'roo-document-manager-thumb',
32405 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32411 this.xhr.open(this.method, this.url, true);
32414 "Accept": "application/json",
32415 "Cache-Control": "no-cache",
32416 "X-Requested-With": "XMLHttpRequest"
32419 for (var headerName in headers) {
32420 var headerValue = headers[headerName];
32422 this.xhr.setRequestHeader(headerName, headerValue);
32428 this.xhr.onload = function()
32430 _this.xhrOnLoad(_this.xhr);
32433 this.xhr.onerror = function()
32435 _this.xhrOnError(_this.xhr);
32438 var formData = new FormData();
32440 formData.append('returnHTML', 'NO');
32442 formData.append('crop', crop);
32444 if(typeof(file.filename) != 'undefined'){
32445 formData.append('filename', file.filename);
32448 if(typeof(file.mimetype) != 'undefined'){
32449 formData.append('mimetype', file.mimetype);
32454 if(this.fireEvent('prepare', this, formData) != false){
32455 this.xhr.send(formData);
32465 * @class Roo.bootstrap.DocumentViewer
32466 * @extends Roo.bootstrap.Component
32467 * Bootstrap DocumentViewer class
32468 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32469 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32472 * Create a new DocumentViewer
32473 * @param {Object} config The config object
32476 Roo.bootstrap.DocumentViewer = function(config){
32477 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32482 * Fire after initEvent
32483 * @param {Roo.bootstrap.DocumentViewer} this
32489 * @param {Roo.bootstrap.DocumentViewer} this
32494 * Fire after download button
32495 * @param {Roo.bootstrap.DocumentViewer} this
32500 * Fire after trash button
32501 * @param {Roo.bootstrap.DocumentViewer} this
32508 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32510 showDownload : true,
32514 getAutoCreate : function()
32518 cls : 'roo-document-viewer',
32522 cls : 'roo-document-viewer-body',
32526 cls : 'roo-document-viewer-thumb',
32530 cls : 'roo-document-viewer-image'
32538 cls : 'roo-document-viewer-footer',
32541 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32545 cls : 'btn-group roo-document-viewer-download',
32549 cls : 'btn btn-default',
32550 html : '<i class="fa fa-download"></i>'
32556 cls : 'btn-group roo-document-viewer-trash',
32560 cls : 'btn btn-default',
32561 html : '<i class="fa fa-trash"></i>'
32574 initEvents : function()
32576 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32577 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32579 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32580 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32582 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32583 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32585 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32586 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32588 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32589 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32591 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32592 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32594 this.bodyEl.on('click', this.onClick, this);
32595 this.downloadBtn.on('click', this.onDownload, this);
32596 this.trashBtn.on('click', this.onTrash, this);
32598 this.downloadBtn.hide();
32599 this.trashBtn.hide();
32601 if(this.showDownload){
32602 this.downloadBtn.show();
32605 if(this.showTrash){
32606 this.trashBtn.show();
32609 if(!this.showDownload && !this.showTrash) {
32610 this.footerEl.hide();
32615 initial : function()
32617 this.fireEvent('initial', this);
32621 onClick : function(e)
32623 e.preventDefault();
32625 this.fireEvent('click', this);
32628 onDownload : function(e)
32630 e.preventDefault();
32632 this.fireEvent('download', this);
32635 onTrash : function(e)
32637 e.preventDefault();
32639 this.fireEvent('trash', this);
32651 * @class Roo.bootstrap.NavProgressBar
32652 * @extends Roo.bootstrap.Component
32653 * Bootstrap NavProgressBar class
32656 * Create a new nav progress bar
32657 * @param {Object} config The config object
32660 Roo.bootstrap.NavProgressBar = function(config){
32661 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32663 this.bullets = this.bullets || [];
32665 // Roo.bootstrap.NavProgressBar.register(this);
32669 * Fires when the active item changes
32670 * @param {Roo.bootstrap.NavProgressBar} this
32671 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32672 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32679 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32684 getAutoCreate : function()
32686 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32690 cls : 'roo-navigation-bar-group',
32694 cls : 'roo-navigation-top-bar'
32698 cls : 'roo-navigation-bullets-bar',
32702 cls : 'roo-navigation-bar'
32709 cls : 'roo-navigation-bottom-bar'
32719 initEvents: function()
32724 onRender : function(ct, position)
32726 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32728 if(this.bullets.length){
32729 Roo.each(this.bullets, function(b){
32738 addItem : function(cfg)
32740 var item = new Roo.bootstrap.NavProgressItem(cfg);
32742 item.parentId = this.id;
32743 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32746 var top = new Roo.bootstrap.Element({
32748 cls : 'roo-navigation-bar-text'
32751 var bottom = new Roo.bootstrap.Element({
32753 cls : 'roo-navigation-bar-text'
32756 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32757 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32759 var topText = new Roo.bootstrap.Element({
32761 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32764 var bottomText = new Roo.bootstrap.Element({
32766 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32769 topText.onRender(top.el, null);
32770 bottomText.onRender(bottom.el, null);
32773 item.bottomEl = bottom;
32776 this.barItems.push(item);
32781 getActive : function()
32783 var active = false;
32785 Roo.each(this.barItems, function(v){
32787 if (!v.isActive()) {
32799 setActiveItem : function(item)
32803 Roo.each(this.barItems, function(v){
32804 if (v.rid == item.rid) {
32808 if (v.isActive()) {
32809 v.setActive(false);
32814 item.setActive(true);
32816 this.fireEvent('changed', this, item, prev);
32819 getBarItem: function(rid)
32823 Roo.each(this.barItems, function(e) {
32824 if (e.rid != rid) {
32835 indexOfItem : function(item)
32839 Roo.each(this.barItems, function(v, i){
32841 if (v.rid != item.rid) {
32852 setActiveNext : function()
32854 var i = this.indexOfItem(this.getActive());
32856 if (i > this.barItems.length) {
32860 this.setActiveItem(this.barItems[i+1]);
32863 setActivePrev : function()
32865 var i = this.indexOfItem(this.getActive());
32871 this.setActiveItem(this.barItems[i-1]);
32874 format : function()
32876 if(!this.barItems.length){
32880 var width = 100 / this.barItems.length;
32882 Roo.each(this.barItems, function(i){
32883 i.el.setStyle('width', width + '%');
32884 i.topEl.el.setStyle('width', width + '%');
32885 i.bottomEl.el.setStyle('width', width + '%');
32894 * Nav Progress Item
32899 * @class Roo.bootstrap.NavProgressItem
32900 * @extends Roo.bootstrap.Component
32901 * Bootstrap NavProgressItem class
32902 * @cfg {String} rid the reference id
32903 * @cfg {Boolean} active (true|false) Is item active default false
32904 * @cfg {Boolean} disabled (true|false) Is item active default false
32905 * @cfg {String} html
32906 * @cfg {String} position (top|bottom) text position default bottom
32907 * @cfg {String} icon show icon instead of number
32910 * Create a new NavProgressItem
32911 * @param {Object} config The config object
32913 Roo.bootstrap.NavProgressItem = function(config){
32914 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32919 * The raw click event for the entire grid.
32920 * @param {Roo.bootstrap.NavProgressItem} this
32921 * @param {Roo.EventObject} e
32928 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32934 position : 'bottom',
32937 getAutoCreate : function()
32939 var iconCls = 'roo-navigation-bar-item-icon';
32941 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32945 cls: 'roo-navigation-bar-item',
32955 cfg.cls += ' active';
32958 cfg.cls += ' disabled';
32964 disable : function()
32966 this.setDisabled(true);
32969 enable : function()
32971 this.setDisabled(false);
32974 initEvents: function()
32976 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32978 this.iconEl.on('click', this.onClick, this);
32981 onClick : function(e)
32983 e.preventDefault();
32989 if(this.fireEvent('click', this, e) === false){
32993 this.parent().setActiveItem(this);
32996 isActive: function ()
32998 return this.active;
33001 setActive : function(state)
33003 if(this.active == state){
33007 this.active = state;
33010 this.el.addClass('active');
33014 this.el.removeClass('active');
33019 setDisabled : function(state)
33021 if(this.disabled == state){
33025 this.disabled = state;
33028 this.el.addClass('disabled');
33032 this.el.removeClass('disabled');
33035 tooltipEl : function()
33037 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33050 * @class Roo.bootstrap.FieldLabel
33051 * @extends Roo.bootstrap.Component
33052 * Bootstrap FieldLabel class
33053 * @cfg {String} html contents of the element
33054 * @cfg {String} tag tag of the element default label
33055 * @cfg {String} cls class of the element
33056 * @cfg {String} target label target
33057 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33058 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33059 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33060 * @cfg {String} iconTooltip default "This field is required"
33061 * @cfg {String} indicatorpos (left|right) default left
33064 * Create a new FieldLabel
33065 * @param {Object} config The config object
33068 Roo.bootstrap.FieldLabel = function(config){
33069 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33074 * Fires after the field has been marked as invalid.
33075 * @param {Roo.form.FieldLabel} this
33076 * @param {String} msg The validation message
33081 * Fires after the field has been validated with no errors.
33082 * @param {Roo.form.FieldLabel} this
33088 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33095 invalidClass : 'has-warning',
33096 validClass : 'has-success',
33097 iconTooltip : 'This field is required',
33098 indicatorpos : 'left',
33100 getAutoCreate : function(){
33103 if (!this.allowBlank) {
33109 cls : 'roo-bootstrap-field-label ' + this.cls,
33114 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33115 tooltip : this.iconTooltip
33124 if(this.indicatorpos == 'right'){
33127 cls : 'roo-bootstrap-field-label ' + this.cls,
33136 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33137 tooltip : this.iconTooltip
33146 initEvents: function()
33148 Roo.bootstrap.Element.superclass.initEvents.call(this);
33150 this.indicator = this.indicatorEl();
33152 if(this.indicator){
33153 this.indicator.removeClass('visible');
33154 this.indicator.addClass('invisible');
33157 Roo.bootstrap.FieldLabel.register(this);
33160 indicatorEl : function()
33162 var indicator = this.el.select('i.roo-required-indicator',true).first();
33173 * Mark this field as valid
33175 markValid : function()
33177 if(this.indicator){
33178 this.indicator.removeClass('visible');
33179 this.indicator.addClass('invisible');
33181 if (Roo.bootstrap.version == 3) {
33182 this.el.removeClass(this.invalidClass);
33183 this.el.addClass(this.validClass);
33185 this.el.removeClass('is-invalid');
33186 this.el.addClass('is-valid');
33190 this.fireEvent('valid', this);
33194 * Mark this field as invalid
33195 * @param {String} msg The validation message
33197 markInvalid : function(msg)
33199 if(this.indicator){
33200 this.indicator.removeClass('invisible');
33201 this.indicator.addClass('visible');
33203 if (Roo.bootstrap.version == 3) {
33204 this.el.removeClass(this.validClass);
33205 this.el.addClass(this.invalidClass);
33207 this.el.removeClass('is-valid');
33208 this.el.addClass('is-invalid');
33212 this.fireEvent('invalid', this, msg);
33218 Roo.apply(Roo.bootstrap.FieldLabel, {
33223 * register a FieldLabel Group
33224 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33226 register : function(label)
33228 if(this.groups.hasOwnProperty(label.target)){
33232 this.groups[label.target] = label;
33236 * fetch a FieldLabel Group based on the target
33237 * @param {string} target
33238 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33240 get: function(target) {
33241 if (typeof(this.groups[target]) == 'undefined') {
33245 return this.groups[target] ;
33254 * page DateSplitField.
33260 * @class Roo.bootstrap.DateSplitField
33261 * @extends Roo.bootstrap.Component
33262 * Bootstrap DateSplitField class
33263 * @cfg {string} fieldLabel - the label associated
33264 * @cfg {Number} labelWidth set the width of label (0-12)
33265 * @cfg {String} labelAlign (top|left)
33266 * @cfg {Boolean} dayAllowBlank (true|false) default false
33267 * @cfg {Boolean} monthAllowBlank (true|false) default false
33268 * @cfg {Boolean} yearAllowBlank (true|false) default false
33269 * @cfg {string} dayPlaceholder
33270 * @cfg {string} monthPlaceholder
33271 * @cfg {string} yearPlaceholder
33272 * @cfg {string} dayFormat default 'd'
33273 * @cfg {string} monthFormat default 'm'
33274 * @cfg {string} yearFormat default 'Y'
33275 * @cfg {Number} labellg set the width of label (1-12)
33276 * @cfg {Number} labelmd set the width of label (1-12)
33277 * @cfg {Number} labelsm set the width of label (1-12)
33278 * @cfg {Number} labelxs set the width of label (1-12)
33282 * Create a new DateSplitField
33283 * @param {Object} config The config object
33286 Roo.bootstrap.DateSplitField = function(config){
33287 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33293 * getting the data of years
33294 * @param {Roo.bootstrap.DateSplitField} this
33295 * @param {Object} years
33300 * getting the data of days
33301 * @param {Roo.bootstrap.DateSplitField} this
33302 * @param {Object} days
33307 * Fires after the field has been marked as invalid.
33308 * @param {Roo.form.Field} this
33309 * @param {String} msg The validation message
33314 * Fires after the field has been validated with no errors.
33315 * @param {Roo.form.Field} this
33321 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33324 labelAlign : 'top',
33326 dayAllowBlank : false,
33327 monthAllowBlank : false,
33328 yearAllowBlank : false,
33329 dayPlaceholder : '',
33330 monthPlaceholder : '',
33331 yearPlaceholder : '',
33335 isFormField : true,
33341 getAutoCreate : function()
33345 cls : 'row roo-date-split-field-group',
33350 cls : 'form-hidden-field roo-date-split-field-group-value',
33356 var labelCls = 'col-md-12';
33357 var contentCls = 'col-md-4';
33359 if(this.fieldLabel){
33363 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33367 html : this.fieldLabel
33372 if(this.labelAlign == 'left'){
33374 if(this.labelWidth > 12){
33375 label.style = "width: " + this.labelWidth + 'px';
33378 if(this.labelWidth < 13 && this.labelmd == 0){
33379 this.labelmd = this.labelWidth;
33382 if(this.labellg > 0){
33383 labelCls = ' col-lg-' + this.labellg;
33384 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33387 if(this.labelmd > 0){
33388 labelCls = ' col-md-' + this.labelmd;
33389 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33392 if(this.labelsm > 0){
33393 labelCls = ' col-sm-' + this.labelsm;
33394 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33397 if(this.labelxs > 0){
33398 labelCls = ' col-xs-' + this.labelxs;
33399 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33403 label.cls += ' ' + labelCls;
33405 cfg.cn.push(label);
33408 Roo.each(['day', 'month', 'year'], function(t){
33411 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33418 inputEl: function ()
33420 return this.el.select('.roo-date-split-field-group-value', true).first();
33423 onRender : function(ct, position)
33427 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33429 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33431 this.dayField = new Roo.bootstrap.ComboBox({
33432 allowBlank : this.dayAllowBlank,
33433 alwaysQuery : true,
33434 displayField : 'value',
33437 forceSelection : true,
33439 placeholder : this.dayPlaceholder,
33440 selectOnFocus : true,
33441 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33442 triggerAction : 'all',
33444 valueField : 'value',
33445 store : new Roo.data.SimpleStore({
33446 data : (function() {
33448 _this.fireEvent('days', _this, days);
33451 fields : [ 'value' ]
33454 select : function (_self, record, index)
33456 _this.setValue(_this.getValue());
33461 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33463 this.monthField = new Roo.bootstrap.MonthField({
33464 after : '<i class=\"fa fa-calendar\"></i>',
33465 allowBlank : this.monthAllowBlank,
33466 placeholder : this.monthPlaceholder,
33469 render : function (_self)
33471 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33472 e.preventDefault();
33476 select : function (_self, oldvalue, newvalue)
33478 _this.setValue(_this.getValue());
33483 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33485 this.yearField = new Roo.bootstrap.ComboBox({
33486 allowBlank : this.yearAllowBlank,
33487 alwaysQuery : true,
33488 displayField : 'value',
33491 forceSelection : true,
33493 placeholder : this.yearPlaceholder,
33494 selectOnFocus : true,
33495 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33496 triggerAction : 'all',
33498 valueField : 'value',
33499 store : new Roo.data.SimpleStore({
33500 data : (function() {
33502 _this.fireEvent('years', _this, years);
33505 fields : [ 'value' ]
33508 select : function (_self, record, index)
33510 _this.setValue(_this.getValue());
33515 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33518 setValue : function(v, format)
33520 this.inputEl.dom.value = v;
33522 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33524 var d = Date.parseDate(v, f);
33531 this.setDay(d.format(this.dayFormat));
33532 this.setMonth(d.format(this.monthFormat));
33533 this.setYear(d.format(this.yearFormat));
33540 setDay : function(v)
33542 this.dayField.setValue(v);
33543 this.inputEl.dom.value = this.getValue();
33548 setMonth : function(v)
33550 this.monthField.setValue(v, true);
33551 this.inputEl.dom.value = this.getValue();
33556 setYear : function(v)
33558 this.yearField.setValue(v);
33559 this.inputEl.dom.value = this.getValue();
33564 getDay : function()
33566 return this.dayField.getValue();
33569 getMonth : function()
33571 return this.monthField.getValue();
33574 getYear : function()
33576 return this.yearField.getValue();
33579 getValue : function()
33581 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33583 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33593 this.inputEl.dom.value = '';
33598 validate : function()
33600 var d = this.dayField.validate();
33601 var m = this.monthField.validate();
33602 var y = this.yearField.validate();
33607 (!this.dayAllowBlank && !d) ||
33608 (!this.monthAllowBlank && !m) ||
33609 (!this.yearAllowBlank && !y)
33614 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33623 this.markInvalid();
33628 markValid : function()
33631 var label = this.el.select('label', true).first();
33632 var icon = this.el.select('i.fa-star', true).first();
33638 this.fireEvent('valid', this);
33642 * Mark this field as invalid
33643 * @param {String} msg The validation message
33645 markInvalid : function(msg)
33648 var label = this.el.select('label', true).first();
33649 var icon = this.el.select('i.fa-star', true).first();
33651 if(label && !icon){
33652 this.el.select('.roo-date-split-field-label', true).createChild({
33654 cls : 'text-danger fa fa-lg fa-star',
33655 tooltip : 'This field is required',
33656 style : 'margin-right:5px;'
33660 this.fireEvent('invalid', this, msg);
33663 clearInvalid : function()
33665 var label = this.el.select('label', true).first();
33666 var icon = this.el.select('i.fa-star', true).first();
33672 this.fireEvent('valid', this);
33675 getName: function()
33685 * http://masonry.desandro.com
33687 * The idea is to render all the bricks based on vertical width...
33689 * The original code extends 'outlayer' - we might need to use that....
33695 * @class Roo.bootstrap.LayoutMasonry
33696 * @extends Roo.bootstrap.Component
33697 * Bootstrap Layout Masonry class
33700 * Create a new Element
33701 * @param {Object} config The config object
33704 Roo.bootstrap.LayoutMasonry = function(config){
33706 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33710 Roo.bootstrap.LayoutMasonry.register(this);
33716 * Fire after layout the items
33717 * @param {Roo.bootstrap.LayoutMasonry} this
33718 * @param {Roo.EventObject} e
33725 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33728 * @cfg {Boolean} isLayoutInstant = no animation?
33730 isLayoutInstant : false, // needed?
33733 * @cfg {Number} boxWidth width of the columns
33738 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33743 * @cfg {Number} padWidth padding below box..
33748 * @cfg {Number} gutter gutter width..
33753 * @cfg {Number} maxCols maximum number of columns
33759 * @cfg {Boolean} isAutoInitial defalut true
33761 isAutoInitial : true,
33766 * @cfg {Boolean} isHorizontal defalut false
33768 isHorizontal : false,
33770 currentSize : null,
33776 bricks: null, //CompositeElement
33780 _isLayoutInited : false,
33782 // isAlternative : false, // only use for vertical layout...
33785 * @cfg {Number} alternativePadWidth padding below box..
33787 alternativePadWidth : 50,
33789 selectedBrick : [],
33791 getAutoCreate : function(){
33793 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33797 cls: 'blog-masonary-wrapper ' + this.cls,
33799 cls : 'mas-boxes masonary'
33806 getChildContainer: function( )
33808 if (this.boxesEl) {
33809 return this.boxesEl;
33812 this.boxesEl = this.el.select('.mas-boxes').first();
33814 return this.boxesEl;
33818 initEvents : function()
33822 if(this.isAutoInitial){
33823 Roo.log('hook children rendered');
33824 this.on('childrenrendered', function() {
33825 Roo.log('children rendered');
33831 initial : function()
33833 this.selectedBrick = [];
33835 this.currentSize = this.el.getBox(true);
33837 Roo.EventManager.onWindowResize(this.resize, this);
33839 if(!this.isAutoInitial){
33847 //this.layout.defer(500,this);
33851 resize : function()
33853 var cs = this.el.getBox(true);
33856 this.currentSize.width == cs.width &&
33857 this.currentSize.x == cs.x &&
33858 this.currentSize.height == cs.height &&
33859 this.currentSize.y == cs.y
33861 Roo.log("no change in with or X or Y");
33865 this.currentSize = cs;
33871 layout : function()
33873 this._resetLayout();
33875 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33877 this.layoutItems( isInstant );
33879 this._isLayoutInited = true;
33881 this.fireEvent('layout', this);
33885 _resetLayout : function()
33887 if(this.isHorizontal){
33888 this.horizontalMeasureColumns();
33892 this.verticalMeasureColumns();
33896 verticalMeasureColumns : function()
33898 this.getContainerWidth();
33900 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33901 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33905 var boxWidth = this.boxWidth + this.padWidth;
33907 if(this.containerWidth < this.boxWidth){
33908 boxWidth = this.containerWidth
33911 var containerWidth = this.containerWidth;
33913 var cols = Math.floor(containerWidth / boxWidth);
33915 this.cols = Math.max( cols, 1 );
33917 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33919 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33921 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33923 this.colWidth = boxWidth + avail - this.padWidth;
33925 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33926 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33929 horizontalMeasureColumns : function()
33931 this.getContainerWidth();
33933 var boxWidth = this.boxWidth;
33935 if(this.containerWidth < boxWidth){
33936 boxWidth = this.containerWidth;
33939 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33941 this.el.setHeight(boxWidth);
33945 getContainerWidth : function()
33947 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33950 layoutItems : function( isInstant )
33952 Roo.log(this.bricks);
33954 var items = Roo.apply([], this.bricks);
33956 if(this.isHorizontal){
33957 this._horizontalLayoutItems( items , isInstant );
33961 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33962 // this._verticalAlternativeLayoutItems( items , isInstant );
33966 this._verticalLayoutItems( items , isInstant );
33970 _verticalLayoutItems : function ( items , isInstant)
33972 if ( !items || !items.length ) {
33977 ['xs', 'xs', 'xs', 'tall'],
33978 ['xs', 'xs', 'tall'],
33979 ['xs', 'xs', 'sm'],
33980 ['xs', 'xs', 'xs'],
33986 ['sm', 'xs', 'xs'],
33990 ['tall', 'xs', 'xs', 'xs'],
33991 ['tall', 'xs', 'xs'],
34003 Roo.each(items, function(item, k){
34005 switch (item.size) {
34006 // these layouts take up a full box,
34017 boxes.push([item]);
34040 var filterPattern = function(box, length)
34048 var pattern = box.slice(0, length);
34052 Roo.each(pattern, function(i){
34053 format.push(i.size);
34056 Roo.each(standard, function(s){
34058 if(String(s) != String(format)){
34067 if(!match && length == 1){
34072 filterPattern(box, length - 1);
34076 queue.push(pattern);
34078 box = box.slice(length, box.length);
34080 filterPattern(box, 4);
34086 Roo.each(boxes, function(box, k){
34092 if(box.length == 1){
34097 filterPattern(box, 4);
34101 this._processVerticalLayoutQueue( queue, isInstant );
34105 // _verticalAlternativeLayoutItems : function( items , isInstant )
34107 // if ( !items || !items.length ) {
34111 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34115 _horizontalLayoutItems : function ( items , isInstant)
34117 if ( !items || !items.length || items.length < 3) {
34123 var eItems = items.slice(0, 3);
34125 items = items.slice(3, items.length);
34128 ['xs', 'xs', 'xs', 'wide'],
34129 ['xs', 'xs', 'wide'],
34130 ['xs', 'xs', 'sm'],
34131 ['xs', 'xs', 'xs'],
34137 ['sm', 'xs', 'xs'],
34141 ['wide', 'xs', 'xs', 'xs'],
34142 ['wide', 'xs', 'xs'],
34155 Roo.each(items, function(item, k){
34157 switch (item.size) {
34168 boxes.push([item]);
34192 var filterPattern = function(box, length)
34200 var pattern = box.slice(0, length);
34204 Roo.each(pattern, function(i){
34205 format.push(i.size);
34208 Roo.each(standard, function(s){
34210 if(String(s) != String(format)){
34219 if(!match && length == 1){
34224 filterPattern(box, length - 1);
34228 queue.push(pattern);
34230 box = box.slice(length, box.length);
34232 filterPattern(box, 4);
34238 Roo.each(boxes, function(box, k){
34244 if(box.length == 1){
34249 filterPattern(box, 4);
34256 var pos = this.el.getBox(true);
34260 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34262 var hit_end = false;
34264 Roo.each(queue, function(box){
34268 Roo.each(box, function(b){
34270 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34280 Roo.each(box, function(b){
34282 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34285 mx = Math.max(mx, b.x);
34289 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34293 Roo.each(box, function(b){
34295 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34309 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34312 /** Sets position of item in DOM
34313 * @param {Element} item
34314 * @param {Number} x - horizontal position
34315 * @param {Number} y - vertical position
34316 * @param {Boolean} isInstant - disables transitions
34318 _processVerticalLayoutQueue : function( queue, isInstant )
34320 var pos = this.el.getBox(true);
34325 for (var i = 0; i < this.cols; i++){
34329 Roo.each(queue, function(box, k){
34331 var col = k % this.cols;
34333 Roo.each(box, function(b,kk){
34335 b.el.position('absolute');
34337 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34338 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34340 if(b.size == 'md-left' || b.size == 'md-right'){
34341 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34342 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34345 b.el.setWidth(width);
34346 b.el.setHeight(height);
34348 b.el.select('iframe',true).setSize(width,height);
34352 for (var i = 0; i < this.cols; i++){
34354 if(maxY[i] < maxY[col]){
34359 col = Math.min(col, i);
34363 x = pos.x + col * (this.colWidth + this.padWidth);
34367 var positions = [];
34369 switch (box.length){
34371 positions = this.getVerticalOneBoxColPositions(x, y, box);
34374 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34377 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34380 positions = this.getVerticalFourBoxColPositions(x, y, box);
34386 Roo.each(box, function(b,kk){
34388 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34390 var sz = b.el.getSize();
34392 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34400 for (var i = 0; i < this.cols; i++){
34401 mY = Math.max(mY, maxY[i]);
34404 this.el.setHeight(mY - pos.y);
34408 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34410 // var pos = this.el.getBox(true);
34413 // var maxX = pos.right;
34415 // var maxHeight = 0;
34417 // Roo.each(items, function(item, k){
34421 // item.el.position('absolute');
34423 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34425 // item.el.setWidth(width);
34427 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34429 // item.el.setHeight(height);
34432 // item.el.setXY([x, y], isInstant ? false : true);
34434 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34437 // y = y + height + this.alternativePadWidth;
34439 // maxHeight = maxHeight + height + this.alternativePadWidth;
34443 // this.el.setHeight(maxHeight);
34447 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34449 var pos = this.el.getBox(true);
34454 var maxX = pos.right;
34456 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34458 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34460 Roo.each(queue, function(box, k){
34462 Roo.each(box, function(b, kk){
34464 b.el.position('absolute');
34466 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34467 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34469 if(b.size == 'md-left' || b.size == 'md-right'){
34470 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34471 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34474 b.el.setWidth(width);
34475 b.el.setHeight(height);
34483 var positions = [];
34485 switch (box.length){
34487 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34490 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34493 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34496 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34502 Roo.each(box, function(b,kk){
34504 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34506 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34514 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34516 Roo.each(eItems, function(b,k){
34518 b.size = (k == 0) ? 'sm' : 'xs';
34519 b.x = (k == 0) ? 2 : 1;
34520 b.y = (k == 0) ? 2 : 1;
34522 b.el.position('absolute');
34524 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34526 b.el.setWidth(width);
34528 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34530 b.el.setHeight(height);
34534 var positions = [];
34537 x : maxX - this.unitWidth * 2 - this.gutter,
34542 x : maxX - this.unitWidth,
34543 y : minY + (this.unitWidth + this.gutter) * 2
34547 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34551 Roo.each(eItems, function(b,k){
34553 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34559 getVerticalOneBoxColPositions : function(x, y, box)
34563 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34565 if(box[0].size == 'md-left'){
34569 if(box[0].size == 'md-right'){
34574 x : x + (this.unitWidth + this.gutter) * rand,
34581 getVerticalTwoBoxColPositions : function(x, y, box)
34585 if(box[0].size == 'xs'){
34589 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34593 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34607 x : x + (this.unitWidth + this.gutter) * 2,
34608 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34615 getVerticalThreeBoxColPositions : function(x, y, box)
34619 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34627 x : x + (this.unitWidth + this.gutter) * 1,
34632 x : x + (this.unitWidth + this.gutter) * 2,
34640 if(box[0].size == 'xs' && box[1].size == 'xs'){
34649 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34653 x : x + (this.unitWidth + this.gutter) * 1,
34667 x : x + (this.unitWidth + this.gutter) * 2,
34672 x : x + (this.unitWidth + this.gutter) * 2,
34673 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34680 getVerticalFourBoxColPositions : function(x, y, box)
34684 if(box[0].size == 'xs'){
34693 y : y + (this.unitHeight + this.gutter) * 1
34698 y : y + (this.unitHeight + this.gutter) * 2
34702 x : x + (this.unitWidth + this.gutter) * 1,
34716 x : x + (this.unitWidth + this.gutter) * 2,
34721 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34722 y : y + (this.unitHeight + this.gutter) * 1
34726 x : x + (this.unitWidth + this.gutter) * 2,
34727 y : y + (this.unitWidth + this.gutter) * 2
34734 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34738 if(box[0].size == 'md-left'){
34740 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34747 if(box[0].size == 'md-right'){
34749 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34750 y : minY + (this.unitWidth + this.gutter) * 1
34756 var rand = Math.floor(Math.random() * (4 - box[0].y));
34759 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34760 y : minY + (this.unitWidth + this.gutter) * rand
34767 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34771 if(box[0].size == 'xs'){
34774 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34779 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34780 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34788 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34793 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34794 y : minY + (this.unitWidth + this.gutter) * 2
34801 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34805 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34808 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34813 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34814 y : minY + (this.unitWidth + this.gutter) * 1
34818 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34819 y : minY + (this.unitWidth + this.gutter) * 2
34826 if(box[0].size == 'xs' && box[1].size == 'xs'){
34829 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34834 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34839 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34840 y : minY + (this.unitWidth + this.gutter) * 1
34848 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34853 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34854 y : minY + (this.unitWidth + this.gutter) * 2
34858 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34859 y : minY + (this.unitWidth + this.gutter) * 2
34866 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34870 if(box[0].size == 'xs'){
34873 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34878 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34883 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),
34888 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34889 y : minY + (this.unitWidth + this.gutter) * 1
34897 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34902 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34903 y : minY + (this.unitWidth + this.gutter) * 2
34907 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34908 y : minY + (this.unitWidth + this.gutter) * 2
34912 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),
34913 y : minY + (this.unitWidth + this.gutter) * 2
34921 * remove a Masonry Brick
34922 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34924 removeBrick : function(brick_id)
34930 for (var i = 0; i<this.bricks.length; i++) {
34931 if (this.bricks[i].id == brick_id) {
34932 this.bricks.splice(i,1);
34933 this.el.dom.removeChild(Roo.get(brick_id).dom);
34940 * adds a Masonry Brick
34941 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34943 addBrick : function(cfg)
34945 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34946 //this.register(cn);
34947 cn.parentId = this.id;
34948 cn.render(this.el);
34953 * register a Masonry Brick
34954 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34957 register : function(brick)
34959 this.bricks.push(brick);
34960 brick.masonryId = this.id;
34964 * clear all the Masonry Brick
34966 clearAll : function()
34969 //this.getChildContainer().dom.innerHTML = "";
34970 this.el.dom.innerHTML = '';
34973 getSelected : function()
34975 if (!this.selectedBrick) {
34979 return this.selectedBrick;
34983 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34987 * register a Masonry Layout
34988 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34991 register : function(layout)
34993 this.groups[layout.id] = layout;
34996 * fetch a Masonry Layout based on the masonry layout ID
34997 * @param {string} the masonry layout to add
34998 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35001 get: function(layout_id) {
35002 if (typeof(this.groups[layout_id]) == 'undefined') {
35005 return this.groups[layout_id] ;
35017 * http://masonry.desandro.com
35019 * The idea is to render all the bricks based on vertical width...
35021 * The original code extends 'outlayer' - we might need to use that....
35027 * @class Roo.bootstrap.LayoutMasonryAuto
35028 * @extends Roo.bootstrap.Component
35029 * Bootstrap Layout Masonry class
35032 * Create a new Element
35033 * @param {Object} config The config object
35036 Roo.bootstrap.LayoutMasonryAuto = function(config){
35037 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35040 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35043 * @cfg {Boolean} isFitWidth - resize the width..
35045 isFitWidth : false, // options..
35047 * @cfg {Boolean} isOriginLeft = left align?
35049 isOriginLeft : true,
35051 * @cfg {Boolean} isOriginTop = top align?
35053 isOriginTop : false,
35055 * @cfg {Boolean} isLayoutInstant = no animation?
35057 isLayoutInstant : false, // needed?
35059 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35061 isResizingContainer : true,
35063 * @cfg {Number} columnWidth width of the columns
35069 * @cfg {Number} maxCols maximum number of columns
35074 * @cfg {Number} padHeight padding below box..
35080 * @cfg {Boolean} isAutoInitial defalut true
35083 isAutoInitial : true,
35089 initialColumnWidth : 0,
35090 currentSize : null,
35092 colYs : null, // array.
35099 bricks: null, //CompositeElement
35100 cols : 0, // array?
35101 // element : null, // wrapped now this.el
35102 _isLayoutInited : null,
35105 getAutoCreate : function(){
35109 cls: 'blog-masonary-wrapper ' + this.cls,
35111 cls : 'mas-boxes masonary'
35118 getChildContainer: function( )
35120 if (this.boxesEl) {
35121 return this.boxesEl;
35124 this.boxesEl = this.el.select('.mas-boxes').first();
35126 return this.boxesEl;
35130 initEvents : function()
35134 if(this.isAutoInitial){
35135 Roo.log('hook children rendered');
35136 this.on('childrenrendered', function() {
35137 Roo.log('children rendered');
35144 initial : function()
35146 this.reloadItems();
35148 this.currentSize = this.el.getBox(true);
35150 /// was window resize... - let's see if this works..
35151 Roo.EventManager.onWindowResize(this.resize, this);
35153 if(!this.isAutoInitial){
35158 this.layout.defer(500,this);
35161 reloadItems: function()
35163 this.bricks = this.el.select('.masonry-brick', true);
35165 this.bricks.each(function(b) {
35166 //Roo.log(b.getSize());
35167 if (!b.attr('originalwidth')) {
35168 b.attr('originalwidth', b.getSize().width);
35173 Roo.log(this.bricks.elements.length);
35176 resize : function()
35179 var cs = this.el.getBox(true);
35181 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35182 Roo.log("no change in with or X");
35185 this.currentSize = cs;
35189 layout : function()
35192 this._resetLayout();
35193 //this._manageStamps();
35195 // don't animate first layout
35196 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35197 this.layoutItems( isInstant );
35199 // flag for initalized
35200 this._isLayoutInited = true;
35203 layoutItems : function( isInstant )
35205 //var items = this._getItemsForLayout( this.items );
35206 // original code supports filtering layout items.. we just ignore it..
35208 this._layoutItems( this.bricks , isInstant );
35210 this._postLayout();
35212 _layoutItems : function ( items , isInstant)
35214 //this.fireEvent( 'layout', this, items );
35217 if ( !items || !items.elements.length ) {
35218 // no items, emit event with empty array
35223 items.each(function(item) {
35224 Roo.log("layout item");
35226 // get x/y object from method
35227 var position = this._getItemLayoutPosition( item );
35229 position.item = item;
35230 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35231 queue.push( position );
35234 this._processLayoutQueue( queue );
35236 /** Sets position of item in DOM
35237 * @param {Element} item
35238 * @param {Number} x - horizontal position
35239 * @param {Number} y - vertical position
35240 * @param {Boolean} isInstant - disables transitions
35242 _processLayoutQueue : function( queue )
35244 for ( var i=0, len = queue.length; i < len; i++ ) {
35245 var obj = queue[i];
35246 obj.item.position('absolute');
35247 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35253 * Any logic you want to do after each layout,
35254 * i.e. size the container
35256 _postLayout : function()
35258 this.resizeContainer();
35261 resizeContainer : function()
35263 if ( !this.isResizingContainer ) {
35266 var size = this._getContainerSize();
35268 this.el.setSize(size.width,size.height);
35269 this.boxesEl.setSize(size.width,size.height);
35275 _resetLayout : function()
35277 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35278 this.colWidth = this.el.getWidth();
35279 //this.gutter = this.el.getWidth();
35281 this.measureColumns();
35287 this.colYs.push( 0 );
35293 measureColumns : function()
35295 this.getContainerWidth();
35296 // if columnWidth is 0, default to outerWidth of first item
35297 if ( !this.columnWidth ) {
35298 var firstItem = this.bricks.first();
35299 Roo.log(firstItem);
35300 this.columnWidth = this.containerWidth;
35301 if (firstItem && firstItem.attr('originalwidth') ) {
35302 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35304 // columnWidth fall back to item of first element
35305 Roo.log("set column width?");
35306 this.initialColumnWidth = this.columnWidth ;
35308 // if first elem has no width, default to size of container
35313 if (this.initialColumnWidth) {
35314 this.columnWidth = this.initialColumnWidth;
35319 // column width is fixed at the top - however if container width get's smaller we should
35322 // this bit calcs how man columns..
35324 var columnWidth = this.columnWidth += this.gutter;
35326 // calculate columns
35327 var containerWidth = this.containerWidth + this.gutter;
35329 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35330 // fix rounding errors, typically with gutters
35331 var excess = columnWidth - containerWidth % columnWidth;
35334 // if overshoot is less than a pixel, round up, otherwise floor it
35335 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35336 cols = Math[ mathMethod ]( cols );
35337 this.cols = Math.max( cols, 1 );
35338 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35340 // padding positioning..
35341 var totalColWidth = this.cols * this.columnWidth;
35342 var padavail = this.containerWidth - totalColWidth;
35343 // so for 2 columns - we need 3 'pads'
35345 var padNeeded = (1+this.cols) * this.padWidth;
35347 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35349 this.columnWidth += padExtra
35350 //this.padWidth = Math.floor(padavail / ( this.cols));
35352 // adjust colum width so that padding is fixed??
35354 // we have 3 columns ... total = width * 3
35355 // we have X left over... that should be used by
35357 //if (this.expandC) {
35365 getContainerWidth : function()
35367 /* // container is parent if fit width
35368 var container = this.isFitWidth ? this.element.parentNode : this.element;
35369 // check that this.size and size are there
35370 // IE8 triggers resize on body size change, so they might not be
35372 var size = getSize( container ); //FIXME
35373 this.containerWidth = size && size.innerWidth; //FIXME
35376 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35380 _getItemLayoutPosition : function( item ) // what is item?
35382 // we resize the item to our columnWidth..
35384 item.setWidth(this.columnWidth);
35385 item.autoBoxAdjust = false;
35387 var sz = item.getSize();
35389 // how many columns does this brick span
35390 var remainder = this.containerWidth % this.columnWidth;
35392 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35393 // round if off by 1 pixel, otherwise use ceil
35394 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35395 colSpan = Math.min( colSpan, this.cols );
35397 // normally this should be '1' as we dont' currently allow multi width columns..
35399 var colGroup = this._getColGroup( colSpan );
35400 // get the minimum Y value from the columns
35401 var minimumY = Math.min.apply( Math, colGroup );
35402 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35404 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35406 // position the brick
35408 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35409 y: this.currentSize.y + minimumY + this.padHeight
35413 // apply setHeight to necessary columns
35414 var setHeight = minimumY + sz.height + this.padHeight;
35415 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35417 var setSpan = this.cols + 1 - colGroup.length;
35418 for ( var i = 0; i < setSpan; i++ ) {
35419 this.colYs[ shortColIndex + i ] = setHeight ;
35426 * @param {Number} colSpan - number of columns the element spans
35427 * @returns {Array} colGroup
35429 _getColGroup : function( colSpan )
35431 if ( colSpan < 2 ) {
35432 // if brick spans only one column, use all the column Ys
35437 // how many different places could this brick fit horizontally
35438 var groupCount = this.cols + 1 - colSpan;
35439 // for each group potential horizontal position
35440 for ( var i = 0; i < groupCount; i++ ) {
35441 // make an array of colY values for that one group
35442 var groupColYs = this.colYs.slice( i, i + colSpan );
35443 // and get the max value of the array
35444 colGroup[i] = Math.max.apply( Math, groupColYs );
35449 _manageStamp : function( stamp )
35451 var stampSize = stamp.getSize();
35452 var offset = stamp.getBox();
35453 // get the columns that this stamp affects
35454 var firstX = this.isOriginLeft ? offset.x : offset.right;
35455 var lastX = firstX + stampSize.width;
35456 var firstCol = Math.floor( firstX / this.columnWidth );
35457 firstCol = Math.max( 0, firstCol );
35459 var lastCol = Math.floor( lastX / this.columnWidth );
35460 // lastCol should not go over if multiple of columnWidth #425
35461 lastCol -= lastX % this.columnWidth ? 0 : 1;
35462 lastCol = Math.min( this.cols - 1, lastCol );
35464 // set colYs to bottom of the stamp
35465 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35468 for ( var i = firstCol; i <= lastCol; i++ ) {
35469 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35474 _getContainerSize : function()
35476 this.maxY = Math.max.apply( Math, this.colYs );
35481 if ( this.isFitWidth ) {
35482 size.width = this._getContainerFitWidth();
35488 _getContainerFitWidth : function()
35490 var unusedCols = 0;
35491 // count unused columns
35494 if ( this.colYs[i] !== 0 ) {
35499 // fit container to columns that have been used
35500 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35503 needsResizeLayout : function()
35505 var previousWidth = this.containerWidth;
35506 this.getContainerWidth();
35507 return previousWidth !== this.containerWidth;
35522 * @class Roo.bootstrap.MasonryBrick
35523 * @extends Roo.bootstrap.Component
35524 * Bootstrap MasonryBrick class
35527 * Create a new MasonryBrick
35528 * @param {Object} config The config object
35531 Roo.bootstrap.MasonryBrick = function(config){
35533 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35535 Roo.bootstrap.MasonryBrick.register(this);
35541 * When a MasonryBrick is clcik
35542 * @param {Roo.bootstrap.MasonryBrick} this
35543 * @param {Roo.EventObject} e
35549 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35552 * @cfg {String} title
35556 * @cfg {String} html
35560 * @cfg {String} bgimage
35564 * @cfg {String} videourl
35568 * @cfg {String} cls
35572 * @cfg {String} href
35576 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35581 * @cfg {String} placetitle (center|bottom)
35586 * @cfg {Boolean} isFitContainer defalut true
35588 isFitContainer : true,
35591 * @cfg {Boolean} preventDefault defalut false
35593 preventDefault : false,
35596 * @cfg {Boolean} inverse defalut false
35598 maskInverse : false,
35600 getAutoCreate : function()
35602 if(!this.isFitContainer){
35603 return this.getSplitAutoCreate();
35606 var cls = 'masonry-brick masonry-brick-full';
35608 if(this.href.length){
35609 cls += ' masonry-brick-link';
35612 if(this.bgimage.length){
35613 cls += ' masonry-brick-image';
35616 if(this.maskInverse){
35617 cls += ' mask-inverse';
35620 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35621 cls += ' enable-mask';
35625 cls += ' masonry-' + this.size + '-brick';
35628 if(this.placetitle.length){
35630 switch (this.placetitle) {
35632 cls += ' masonry-center-title';
35635 cls += ' masonry-bottom-title';
35642 if(!this.html.length && !this.bgimage.length){
35643 cls += ' masonry-center-title';
35646 if(!this.html.length && this.bgimage.length){
35647 cls += ' masonry-bottom-title';
35652 cls += ' ' + this.cls;
35656 tag: (this.href.length) ? 'a' : 'div',
35661 cls: 'masonry-brick-mask'
35665 cls: 'masonry-brick-paragraph',
35671 if(this.href.length){
35672 cfg.href = this.href;
35675 var cn = cfg.cn[1].cn;
35677 if(this.title.length){
35680 cls: 'masonry-brick-title',
35685 if(this.html.length){
35688 cls: 'masonry-brick-text',
35693 if (!this.title.length && !this.html.length) {
35694 cfg.cn[1].cls += ' hide';
35697 if(this.bgimage.length){
35700 cls: 'masonry-brick-image-view',
35705 if(this.videourl.length){
35706 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35707 // youtube support only?
35710 cls: 'masonry-brick-image-view',
35713 allowfullscreen : true
35721 getSplitAutoCreate : function()
35723 var cls = 'masonry-brick masonry-brick-split';
35725 if(this.href.length){
35726 cls += ' masonry-brick-link';
35729 if(this.bgimage.length){
35730 cls += ' masonry-brick-image';
35734 cls += ' masonry-' + this.size + '-brick';
35737 switch (this.placetitle) {
35739 cls += ' masonry-center-title';
35742 cls += ' masonry-bottom-title';
35745 if(!this.bgimage.length){
35746 cls += ' masonry-center-title';
35749 if(this.bgimage.length){
35750 cls += ' masonry-bottom-title';
35756 cls += ' ' + this.cls;
35760 tag: (this.href.length) ? 'a' : 'div',
35765 cls: 'masonry-brick-split-head',
35769 cls: 'masonry-brick-paragraph',
35776 cls: 'masonry-brick-split-body',
35782 if(this.href.length){
35783 cfg.href = this.href;
35786 if(this.title.length){
35787 cfg.cn[0].cn[0].cn.push({
35789 cls: 'masonry-brick-title',
35794 if(this.html.length){
35795 cfg.cn[1].cn.push({
35797 cls: 'masonry-brick-text',
35802 if(this.bgimage.length){
35803 cfg.cn[0].cn.push({
35805 cls: 'masonry-brick-image-view',
35810 if(this.videourl.length){
35811 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35812 // youtube support only?
35813 cfg.cn[0].cn.cn.push({
35815 cls: 'masonry-brick-image-view',
35818 allowfullscreen : true
35825 initEvents: function()
35827 switch (this.size) {
35860 this.el.on('touchstart', this.onTouchStart, this);
35861 this.el.on('touchmove', this.onTouchMove, this);
35862 this.el.on('touchend', this.onTouchEnd, this);
35863 this.el.on('contextmenu', this.onContextMenu, this);
35865 this.el.on('mouseenter' ,this.enter, this);
35866 this.el.on('mouseleave', this.leave, this);
35867 this.el.on('click', this.onClick, this);
35870 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35871 this.parent().bricks.push(this);
35876 onClick: function(e, el)
35878 var time = this.endTimer - this.startTimer;
35879 // Roo.log(e.preventDefault());
35882 e.preventDefault();
35887 if(!this.preventDefault){
35891 e.preventDefault();
35893 if (this.activeClass != '') {
35894 this.selectBrick();
35897 this.fireEvent('click', this, e);
35900 enter: function(e, el)
35902 e.preventDefault();
35904 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35908 if(this.bgimage.length && this.html.length){
35909 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35913 leave: function(e, el)
35915 e.preventDefault();
35917 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35921 if(this.bgimage.length && this.html.length){
35922 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35926 onTouchStart: function(e, el)
35928 // e.preventDefault();
35930 this.touchmoved = false;
35932 if(!this.isFitContainer){
35936 if(!this.bgimage.length || !this.html.length){
35940 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35942 this.timer = new Date().getTime();
35946 onTouchMove: function(e, el)
35948 this.touchmoved = true;
35951 onContextMenu : function(e,el)
35953 e.preventDefault();
35954 e.stopPropagation();
35958 onTouchEnd: function(e, el)
35960 // e.preventDefault();
35962 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35969 if(!this.bgimage.length || !this.html.length){
35971 if(this.href.length){
35972 window.location.href = this.href;
35978 if(!this.isFitContainer){
35982 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35984 window.location.href = this.href;
35987 //selection on single brick only
35988 selectBrick : function() {
35990 if (!this.parentId) {
35994 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35995 var index = m.selectedBrick.indexOf(this.id);
35998 m.selectedBrick.splice(index,1);
35999 this.el.removeClass(this.activeClass);
36003 for(var i = 0; i < m.selectedBrick.length; i++) {
36004 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36005 b.el.removeClass(b.activeClass);
36008 m.selectedBrick = [];
36010 m.selectedBrick.push(this.id);
36011 this.el.addClass(this.activeClass);
36015 isSelected : function(){
36016 return this.el.hasClass(this.activeClass);
36021 Roo.apply(Roo.bootstrap.MasonryBrick, {
36024 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36026 * register a Masonry Brick
36027 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36030 register : function(brick)
36032 //this.groups[brick.id] = brick;
36033 this.groups.add(brick.id, brick);
36036 * fetch a masonry brick based on the masonry brick ID
36037 * @param {string} the masonry brick to add
36038 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36041 get: function(brick_id)
36043 // if (typeof(this.groups[brick_id]) == 'undefined') {
36046 // return this.groups[brick_id] ;
36048 if(this.groups.key(brick_id)) {
36049 return this.groups.key(brick_id);
36067 * @class Roo.bootstrap.Brick
36068 * @extends Roo.bootstrap.Component
36069 * Bootstrap Brick class
36072 * Create a new Brick
36073 * @param {Object} config The config object
36076 Roo.bootstrap.Brick = function(config){
36077 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36083 * When a Brick is click
36084 * @param {Roo.bootstrap.Brick} this
36085 * @param {Roo.EventObject} e
36091 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36094 * @cfg {String} title
36098 * @cfg {String} html
36102 * @cfg {String} bgimage
36106 * @cfg {String} cls
36110 * @cfg {String} href
36114 * @cfg {String} video
36118 * @cfg {Boolean} square
36122 getAutoCreate : function()
36124 var cls = 'roo-brick';
36126 if(this.href.length){
36127 cls += ' roo-brick-link';
36130 if(this.bgimage.length){
36131 cls += ' roo-brick-image';
36134 if(!this.html.length && !this.bgimage.length){
36135 cls += ' roo-brick-center-title';
36138 if(!this.html.length && this.bgimage.length){
36139 cls += ' roo-brick-bottom-title';
36143 cls += ' ' + this.cls;
36147 tag: (this.href.length) ? 'a' : 'div',
36152 cls: 'roo-brick-paragraph',
36158 if(this.href.length){
36159 cfg.href = this.href;
36162 var cn = cfg.cn[0].cn;
36164 if(this.title.length){
36167 cls: 'roo-brick-title',
36172 if(this.html.length){
36175 cls: 'roo-brick-text',
36182 if(this.bgimage.length){
36185 cls: 'roo-brick-image-view',
36193 initEvents: function()
36195 if(this.title.length || this.html.length){
36196 this.el.on('mouseenter' ,this.enter, this);
36197 this.el.on('mouseleave', this.leave, this);
36200 Roo.EventManager.onWindowResize(this.resize, this);
36202 if(this.bgimage.length){
36203 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36204 this.imageEl.on('load', this.onImageLoad, this);
36211 onImageLoad : function()
36216 resize : function()
36218 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36220 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36222 if(this.bgimage.length){
36223 var image = this.el.select('.roo-brick-image-view', true).first();
36225 image.setWidth(paragraph.getWidth());
36228 image.setHeight(paragraph.getWidth());
36231 this.el.setHeight(image.getHeight());
36232 paragraph.setHeight(image.getHeight());
36238 enter: function(e, el)
36240 e.preventDefault();
36242 if(this.bgimage.length){
36243 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36244 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36248 leave: function(e, el)
36250 e.preventDefault();
36252 if(this.bgimage.length){
36253 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36254 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36269 * @class Roo.bootstrap.NumberField
36270 * @extends Roo.bootstrap.Input
36271 * Bootstrap NumberField class
36277 * Create a new NumberField
36278 * @param {Object} config The config object
36281 Roo.bootstrap.NumberField = function(config){
36282 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36285 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36288 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36290 allowDecimals : true,
36292 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36294 decimalSeparator : ".",
36296 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36298 decimalPrecision : 2,
36300 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36302 allowNegative : true,
36305 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36309 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36311 minValue : Number.NEGATIVE_INFINITY,
36313 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36315 maxValue : Number.MAX_VALUE,
36317 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36319 minText : "The minimum value for this field is {0}",
36321 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36323 maxText : "The maximum value for this field is {0}",
36325 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36326 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36328 nanText : "{0} is not a valid number",
36330 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36332 thousandsDelimiter : false,
36334 * @cfg {String} valueAlign alignment of value
36336 valueAlign : "left",
36338 getAutoCreate : function()
36340 var hiddenInput = {
36344 cls: 'hidden-number-input'
36348 hiddenInput.name = this.name;
36353 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36355 this.name = hiddenInput.name;
36357 if(cfg.cn.length > 0) {
36358 cfg.cn.push(hiddenInput);
36365 initEvents : function()
36367 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36369 var allowed = "0123456789";
36371 if(this.allowDecimals){
36372 allowed += this.decimalSeparator;
36375 if(this.allowNegative){
36379 if(this.thousandsDelimiter) {
36383 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36385 var keyPress = function(e){
36387 var k = e.getKey();
36389 var c = e.getCharCode();
36392 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36393 allowed.indexOf(String.fromCharCode(c)) === -1
36399 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36403 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36408 this.el.on("keypress", keyPress, this);
36411 validateValue : function(value)
36414 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36418 var num = this.parseValue(value);
36421 this.markInvalid(String.format(this.nanText, value));
36425 if(num < this.minValue){
36426 this.markInvalid(String.format(this.minText, this.minValue));
36430 if(num > this.maxValue){
36431 this.markInvalid(String.format(this.maxText, this.maxValue));
36438 getValue : function()
36440 var v = this.hiddenEl().getValue();
36442 return this.fixPrecision(this.parseValue(v));
36445 parseValue : function(value)
36447 if(this.thousandsDelimiter) {
36449 r = new RegExp(",", "g");
36450 value = value.replace(r, "");
36453 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36454 return isNaN(value) ? '' : value;
36457 fixPrecision : function(value)
36459 if(this.thousandsDelimiter) {
36461 r = new RegExp(",", "g");
36462 value = value.replace(r, "");
36465 var nan = isNaN(value);
36467 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36468 return nan ? '' : value;
36470 return parseFloat(value).toFixed(this.decimalPrecision);
36473 setValue : function(v)
36475 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36481 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36483 this.inputEl().dom.value = (v == '') ? '' :
36484 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36486 if(!this.allowZero && v === '0') {
36487 this.hiddenEl().dom.value = '';
36488 this.inputEl().dom.value = '';
36495 decimalPrecisionFcn : function(v)
36497 return Math.floor(v);
36500 beforeBlur : function()
36502 var v = this.parseValue(this.getRawValue());
36504 if(v || v === 0 || v === ''){
36509 hiddenEl : function()
36511 return this.el.select('input.hidden-number-input',true).first();
36523 * @class Roo.bootstrap.DocumentSlider
36524 * @extends Roo.bootstrap.Component
36525 * Bootstrap DocumentSlider class
36528 * Create a new DocumentViewer
36529 * @param {Object} config The config object
36532 Roo.bootstrap.DocumentSlider = function(config){
36533 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36540 * Fire after initEvent
36541 * @param {Roo.bootstrap.DocumentSlider} this
36546 * Fire after update
36547 * @param {Roo.bootstrap.DocumentSlider} this
36553 * @param {Roo.bootstrap.DocumentSlider} this
36559 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36565 getAutoCreate : function()
36569 cls : 'roo-document-slider',
36573 cls : 'roo-document-slider-header',
36577 cls : 'roo-document-slider-header-title'
36583 cls : 'roo-document-slider-body',
36587 cls : 'roo-document-slider-prev',
36591 cls : 'fa fa-chevron-left'
36597 cls : 'roo-document-slider-thumb',
36601 cls : 'roo-document-slider-image'
36607 cls : 'roo-document-slider-next',
36611 cls : 'fa fa-chevron-right'
36623 initEvents : function()
36625 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36626 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36628 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36629 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36631 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36632 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36634 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36635 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36637 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36638 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36640 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36641 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36643 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36644 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36646 this.thumbEl.on('click', this.onClick, this);
36648 this.prevIndicator.on('click', this.prev, this);
36650 this.nextIndicator.on('click', this.next, this);
36654 initial : function()
36656 if(this.files.length){
36657 this.indicator = 1;
36661 this.fireEvent('initial', this);
36664 update : function()
36666 this.imageEl.attr('src', this.files[this.indicator - 1]);
36668 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36670 this.prevIndicator.show();
36672 if(this.indicator == 1){
36673 this.prevIndicator.hide();
36676 this.nextIndicator.show();
36678 if(this.indicator == this.files.length){
36679 this.nextIndicator.hide();
36682 this.thumbEl.scrollTo('top');
36684 this.fireEvent('update', this);
36687 onClick : function(e)
36689 e.preventDefault();
36691 this.fireEvent('click', this);
36696 e.preventDefault();
36698 this.indicator = Math.max(1, this.indicator - 1);
36705 e.preventDefault();
36707 this.indicator = Math.min(this.files.length, this.indicator + 1);
36721 * @class Roo.bootstrap.RadioSet
36722 * @extends Roo.bootstrap.Input
36723 * Bootstrap RadioSet class
36724 * @cfg {String} indicatorpos (left|right) default left
36725 * @cfg {Boolean} inline (true|false) inline the element (default true)
36726 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36728 * Create a new RadioSet
36729 * @param {Object} config The config object
36732 Roo.bootstrap.RadioSet = function(config){
36734 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36738 Roo.bootstrap.RadioSet.register(this);
36743 * Fires when the element is checked or unchecked.
36744 * @param {Roo.bootstrap.RadioSet} this This radio
36745 * @param {Roo.bootstrap.Radio} item The checked item
36750 * Fires when the element is click.
36751 * @param {Roo.bootstrap.RadioSet} this This radio set
36752 * @param {Roo.bootstrap.Radio} item The checked item
36753 * @param {Roo.EventObject} e The event object
36760 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36768 indicatorpos : 'left',
36770 getAutoCreate : function()
36774 cls : 'roo-radio-set-label',
36778 html : this.fieldLabel
36782 if (Roo.bootstrap.version == 3) {
36785 if(this.indicatorpos == 'left'){
36788 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36789 tooltip : 'This field is required'
36794 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36795 tooltip : 'This field is required'
36801 cls : 'roo-radio-set-items'
36804 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36806 if (align === 'left' && this.fieldLabel.length) {
36809 cls : "roo-radio-set-right",
36815 if(this.labelWidth > 12){
36816 label.style = "width: " + this.labelWidth + 'px';
36819 if(this.labelWidth < 13 && this.labelmd == 0){
36820 this.labelmd = this.labelWidth;
36823 if(this.labellg > 0){
36824 label.cls += ' col-lg-' + this.labellg;
36825 items.cls += ' col-lg-' + (12 - this.labellg);
36828 if(this.labelmd > 0){
36829 label.cls += ' col-md-' + this.labelmd;
36830 items.cls += ' col-md-' + (12 - this.labelmd);
36833 if(this.labelsm > 0){
36834 label.cls += ' col-sm-' + this.labelsm;
36835 items.cls += ' col-sm-' + (12 - this.labelsm);
36838 if(this.labelxs > 0){
36839 label.cls += ' col-xs-' + this.labelxs;
36840 items.cls += ' col-xs-' + (12 - this.labelxs);
36846 cls : 'roo-radio-set',
36850 cls : 'roo-radio-set-input',
36853 value : this.value ? this.value : ''
36860 if(this.weight.length){
36861 cfg.cls += ' roo-radio-' + this.weight;
36865 cfg.cls += ' roo-radio-set-inline';
36869 ['xs','sm','md','lg'].map(function(size){
36870 if (settings[size]) {
36871 cfg.cls += ' col-' + size + '-' + settings[size];
36879 initEvents : function()
36881 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36882 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36884 if(!this.fieldLabel.length){
36885 this.labelEl.hide();
36888 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36889 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36891 this.indicator = this.indicatorEl();
36893 if(this.indicator){
36894 this.indicator.addClass('invisible');
36897 this.originalValue = this.getValue();
36901 inputEl: function ()
36903 return this.el.select('.roo-radio-set-input', true).first();
36906 getChildContainer : function()
36908 return this.itemsEl;
36911 register : function(item)
36913 this.radioes.push(item);
36917 validate : function()
36919 if(this.getVisibilityEl().hasClass('hidden')){
36925 Roo.each(this.radioes, function(i){
36934 if(this.allowBlank) {
36938 if(this.disabled || valid){
36943 this.markInvalid();
36948 markValid : function()
36950 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36951 this.indicatorEl().removeClass('visible');
36952 this.indicatorEl().addClass('invisible');
36956 if (Roo.bootstrap.version == 3) {
36957 this.el.removeClass([this.invalidClass, this.validClass]);
36958 this.el.addClass(this.validClass);
36960 this.el.removeClass(['is-invalid','is-valid']);
36961 this.el.addClass(['is-valid']);
36963 this.fireEvent('valid', this);
36966 markInvalid : function(msg)
36968 if(this.allowBlank || this.disabled){
36972 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36973 this.indicatorEl().removeClass('invisible');
36974 this.indicatorEl().addClass('visible');
36976 if (Roo.bootstrap.version == 3) {
36977 this.el.removeClass([this.invalidClass, this.validClass]);
36978 this.el.addClass(this.invalidClass);
36980 this.el.removeClass(['is-invalid','is-valid']);
36981 this.el.addClass(['is-invalid']);
36984 this.fireEvent('invalid', this, msg);
36988 setValue : function(v, suppressEvent)
36990 if(this.value === v){
36997 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37000 Roo.each(this.radioes, function(i){
37002 i.el.removeClass('checked');
37005 Roo.each(this.radioes, function(i){
37007 if(i.value === v || i.value.toString() === v.toString()){
37009 i.el.addClass('checked');
37011 if(suppressEvent !== true){
37012 this.fireEvent('check', this, i);
37023 clearInvalid : function(){
37025 if(!this.el || this.preventMark){
37029 this.el.removeClass([this.invalidClass]);
37031 this.fireEvent('valid', this);
37036 Roo.apply(Roo.bootstrap.RadioSet, {
37040 register : function(set)
37042 this.groups[set.name] = set;
37045 get: function(name)
37047 if (typeof(this.groups[name]) == 'undefined') {
37051 return this.groups[name] ;
37057 * Ext JS Library 1.1.1
37058 * Copyright(c) 2006-2007, Ext JS, LLC.
37060 * Originally Released Under LGPL - original licence link has changed is not relivant.
37063 * <script type="text/javascript">
37068 * @class Roo.bootstrap.SplitBar
37069 * @extends Roo.util.Observable
37070 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37074 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37075 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37076 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37077 split.minSize = 100;
37078 split.maxSize = 600;
37079 split.animate = true;
37080 split.on('moved', splitterMoved);
37083 * Create a new SplitBar
37084 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37085 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37086 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37087 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37088 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37089 position of the SplitBar).
37091 Roo.bootstrap.SplitBar = function(cfg){
37096 // dragElement : elm
37097 // resizingElement: el,
37099 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37100 // placement : Roo.bootstrap.SplitBar.LEFT ,
37101 // existingProxy ???
37104 this.el = Roo.get(cfg.dragElement, true);
37105 this.el.dom.unselectable = "on";
37107 this.resizingEl = Roo.get(cfg.resizingElement, true);
37111 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37112 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37115 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37118 * The minimum size of the resizing element. (Defaults to 0)
37124 * The maximum size of the resizing element. (Defaults to 2000)
37127 this.maxSize = 2000;
37130 * Whether to animate the transition to the new size
37133 this.animate = false;
37136 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37139 this.useShim = false;
37144 if(!cfg.existingProxy){
37146 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37148 this.proxy = Roo.get(cfg.existingProxy).dom;
37151 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37154 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37157 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37160 this.dragSpecs = {};
37163 * @private The adapter to use to positon and resize elements
37165 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37166 this.adapter.init(this);
37168 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37170 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37171 this.el.addClass("roo-splitbar-h");
37174 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37175 this.el.addClass("roo-splitbar-v");
37181 * Fires when the splitter is moved (alias for {@link #event-moved})
37182 * @param {Roo.bootstrap.SplitBar} this
37183 * @param {Number} newSize the new width or height
37188 * Fires when the splitter is moved
37189 * @param {Roo.bootstrap.SplitBar} this
37190 * @param {Number} newSize the new width or height
37194 * @event beforeresize
37195 * Fires before the splitter is dragged
37196 * @param {Roo.bootstrap.SplitBar} this
37198 "beforeresize" : true,
37200 "beforeapply" : true
37203 Roo.util.Observable.call(this);
37206 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37207 onStartProxyDrag : function(x, y){
37208 this.fireEvent("beforeresize", this);
37210 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37212 o.enableDisplayMode("block");
37213 // all splitbars share the same overlay
37214 Roo.bootstrap.SplitBar.prototype.overlay = o;
37216 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37217 this.overlay.show();
37218 Roo.get(this.proxy).setDisplayed("block");
37219 var size = this.adapter.getElementSize(this);
37220 this.activeMinSize = this.getMinimumSize();;
37221 this.activeMaxSize = this.getMaximumSize();;
37222 var c1 = size - this.activeMinSize;
37223 var c2 = Math.max(this.activeMaxSize - size, 0);
37224 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37225 this.dd.resetConstraints();
37226 this.dd.setXConstraint(
37227 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37228 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37230 this.dd.setYConstraint(0, 0);
37232 this.dd.resetConstraints();
37233 this.dd.setXConstraint(0, 0);
37234 this.dd.setYConstraint(
37235 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37236 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37239 this.dragSpecs.startSize = size;
37240 this.dragSpecs.startPoint = [x, y];
37241 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37245 * @private Called after the drag operation by the DDProxy
37247 onEndProxyDrag : function(e){
37248 Roo.get(this.proxy).setDisplayed(false);
37249 var endPoint = Roo.lib.Event.getXY(e);
37251 this.overlay.hide();
37254 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37255 newSize = this.dragSpecs.startSize +
37256 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37257 endPoint[0] - this.dragSpecs.startPoint[0] :
37258 this.dragSpecs.startPoint[0] - endPoint[0]
37261 newSize = this.dragSpecs.startSize +
37262 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37263 endPoint[1] - this.dragSpecs.startPoint[1] :
37264 this.dragSpecs.startPoint[1] - endPoint[1]
37267 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37268 if(newSize != this.dragSpecs.startSize){
37269 if(this.fireEvent('beforeapply', this, newSize) !== false){
37270 this.adapter.setElementSize(this, newSize);
37271 this.fireEvent("moved", this, newSize);
37272 this.fireEvent("resize", this, newSize);
37278 * Get the adapter this SplitBar uses
37279 * @return The adapter object
37281 getAdapter : function(){
37282 return this.adapter;
37286 * Set the adapter this SplitBar uses
37287 * @param {Object} adapter A SplitBar adapter object
37289 setAdapter : function(adapter){
37290 this.adapter = adapter;
37291 this.adapter.init(this);
37295 * Gets the minimum size for the resizing element
37296 * @return {Number} The minimum size
37298 getMinimumSize : function(){
37299 return this.minSize;
37303 * Sets the minimum size for the resizing element
37304 * @param {Number} minSize The minimum size
37306 setMinimumSize : function(minSize){
37307 this.minSize = minSize;
37311 * Gets the maximum size for the resizing element
37312 * @return {Number} The maximum size
37314 getMaximumSize : function(){
37315 return this.maxSize;
37319 * Sets the maximum size for the resizing element
37320 * @param {Number} maxSize The maximum size
37322 setMaximumSize : function(maxSize){
37323 this.maxSize = maxSize;
37327 * Sets the initialize size for the resizing element
37328 * @param {Number} size The initial size
37330 setCurrentSize : function(size){
37331 var oldAnimate = this.animate;
37332 this.animate = false;
37333 this.adapter.setElementSize(this, size);
37334 this.animate = oldAnimate;
37338 * Destroy this splitbar.
37339 * @param {Boolean} removeEl True to remove the element
37341 destroy : function(removeEl){
37343 this.shim.remove();
37346 this.proxy.parentNode.removeChild(this.proxy);
37354 * @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.
37356 Roo.bootstrap.SplitBar.createProxy = function(dir){
37357 var proxy = new Roo.Element(document.createElement("div"));
37358 proxy.unselectable();
37359 var cls = 'roo-splitbar-proxy';
37360 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37361 document.body.appendChild(proxy.dom);
37366 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37367 * Default Adapter. It assumes the splitter and resizing element are not positioned
37368 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37370 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37373 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37374 // do nothing for now
37375 init : function(s){
37379 * Called before drag operations to get the current size of the resizing element.
37380 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37382 getElementSize : function(s){
37383 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37384 return s.resizingEl.getWidth();
37386 return s.resizingEl.getHeight();
37391 * Called after drag operations to set the size of the resizing element.
37392 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37393 * @param {Number} newSize The new size to set
37394 * @param {Function} onComplete A function to be invoked when resizing is complete
37396 setElementSize : function(s, newSize, onComplete){
37397 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37399 s.resizingEl.setWidth(newSize);
37401 onComplete(s, newSize);
37404 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37409 s.resizingEl.setHeight(newSize);
37411 onComplete(s, newSize);
37414 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37421 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37422 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37423 * Adapter that moves the splitter element to align with the resized sizing element.
37424 * Used with an absolute positioned SplitBar.
37425 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37426 * document.body, make sure you assign an id to the body element.
37428 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37429 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37430 this.container = Roo.get(container);
37433 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37434 init : function(s){
37435 this.basic.init(s);
37438 getElementSize : function(s){
37439 return this.basic.getElementSize(s);
37442 setElementSize : function(s, newSize, onComplete){
37443 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37446 moveSplitter : function(s){
37447 var yes = Roo.bootstrap.SplitBar;
37448 switch(s.placement){
37450 s.el.setX(s.resizingEl.getRight());
37453 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37456 s.el.setY(s.resizingEl.getBottom());
37459 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37466 * Orientation constant - Create a vertical SplitBar
37470 Roo.bootstrap.SplitBar.VERTICAL = 1;
37473 * Orientation constant - Create a horizontal SplitBar
37477 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37480 * Placement constant - The resizing element is to the left of the splitter element
37484 Roo.bootstrap.SplitBar.LEFT = 1;
37487 * Placement constant - The resizing element is to the right of the splitter element
37491 Roo.bootstrap.SplitBar.RIGHT = 2;
37494 * Placement constant - The resizing element is positioned above the splitter element
37498 Roo.bootstrap.SplitBar.TOP = 3;
37501 * Placement constant - The resizing element is positioned under splitter element
37505 Roo.bootstrap.SplitBar.BOTTOM = 4;
37506 Roo.namespace("Roo.bootstrap.layout");/*
37508 * Ext JS Library 1.1.1
37509 * Copyright(c) 2006-2007, Ext JS, LLC.
37511 * Originally Released Under LGPL - original licence link has changed is not relivant.
37514 * <script type="text/javascript">
37518 * @class Roo.bootstrap.layout.Manager
37519 * @extends Roo.bootstrap.Component
37520 * Base class for layout managers.
37522 Roo.bootstrap.layout.Manager = function(config)
37524 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37530 /** false to disable window resize monitoring @type Boolean */
37531 this.monitorWindowResize = true;
37536 * Fires when a layout is performed.
37537 * @param {Roo.LayoutManager} this
37541 * @event regionresized
37542 * Fires when the user resizes a region.
37543 * @param {Roo.LayoutRegion} region The resized region
37544 * @param {Number} newSize The new size (width for east/west, height for north/south)
37546 "regionresized" : true,
37548 * @event regioncollapsed
37549 * Fires when a region is collapsed.
37550 * @param {Roo.LayoutRegion} region The collapsed region
37552 "regioncollapsed" : true,
37554 * @event regionexpanded
37555 * Fires when a region is expanded.
37556 * @param {Roo.LayoutRegion} region The expanded region
37558 "regionexpanded" : true
37560 this.updating = false;
37563 this.el = Roo.get(config.el);
37569 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37574 monitorWindowResize : true,
37580 onRender : function(ct, position)
37583 this.el = Roo.get(ct);
37586 //this.fireEvent('render',this);
37590 initEvents: function()
37594 // ie scrollbar fix
37595 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37596 document.body.scroll = "no";
37597 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37598 this.el.position('relative');
37600 this.id = this.el.id;
37601 this.el.addClass("roo-layout-container");
37602 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37603 if(this.el.dom != document.body ) {
37604 this.el.on('resize', this.layout,this);
37605 this.el.on('show', this.layout,this);
37611 * Returns true if this layout is currently being updated
37612 * @return {Boolean}
37614 isUpdating : function(){
37615 return this.updating;
37619 * Suspend the LayoutManager from doing auto-layouts while
37620 * making multiple add or remove calls
37622 beginUpdate : function(){
37623 this.updating = true;
37627 * Restore auto-layouts and optionally disable the manager from performing a layout
37628 * @param {Boolean} noLayout true to disable a layout update
37630 endUpdate : function(noLayout){
37631 this.updating = false;
37637 layout: function(){
37641 onRegionResized : function(region, newSize){
37642 this.fireEvent("regionresized", region, newSize);
37646 onRegionCollapsed : function(region){
37647 this.fireEvent("regioncollapsed", region);
37650 onRegionExpanded : function(region){
37651 this.fireEvent("regionexpanded", region);
37655 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37656 * performs box-model adjustments.
37657 * @return {Object} The size as an object {width: (the width), height: (the height)}
37659 getViewSize : function()
37662 if(this.el.dom != document.body){
37663 size = this.el.getSize();
37665 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37667 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37668 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37673 * Returns the Element this layout is bound to.
37674 * @return {Roo.Element}
37676 getEl : function(){
37681 * Returns the specified region.
37682 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37683 * @return {Roo.LayoutRegion}
37685 getRegion : function(target){
37686 return this.regions[target.toLowerCase()];
37689 onWindowResize : function(){
37690 if(this.monitorWindowResize){
37697 * Ext JS Library 1.1.1
37698 * Copyright(c) 2006-2007, Ext JS, LLC.
37700 * Originally Released Under LGPL - original licence link has changed is not relivant.
37703 * <script type="text/javascript">
37706 * @class Roo.bootstrap.layout.Border
37707 * @extends Roo.bootstrap.layout.Manager
37708 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37709 * please see: examples/bootstrap/nested.html<br><br>
37711 <b>The container the layout is rendered into can be either the body element or any other element.
37712 If it is not the body element, the container needs to either be an absolute positioned element,
37713 or you will need to add "position:relative" to the css of the container. You will also need to specify
37714 the container size if it is not the body element.</b>
37717 * Create a new Border
37718 * @param {Object} config Configuration options
37720 Roo.bootstrap.layout.Border = function(config){
37721 config = config || {};
37722 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37726 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37727 if(config[region]){
37728 config[region].region = region;
37729 this.addRegion(config[region]);
37735 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37737 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37739 parent : false, // this might point to a 'nest' or a ???
37742 * Creates and adds a new region if it doesn't already exist.
37743 * @param {String} target The target region key (north, south, east, west or center).
37744 * @param {Object} config The regions config object
37745 * @return {BorderLayoutRegion} The new region
37747 addRegion : function(config)
37749 if(!this.regions[config.region]){
37750 var r = this.factory(config);
37751 this.bindRegion(r);
37753 return this.regions[config.region];
37757 bindRegion : function(r){
37758 this.regions[r.config.region] = r;
37760 r.on("visibilitychange", this.layout, this);
37761 r.on("paneladded", this.layout, this);
37762 r.on("panelremoved", this.layout, this);
37763 r.on("invalidated", this.layout, this);
37764 r.on("resized", this.onRegionResized, this);
37765 r.on("collapsed", this.onRegionCollapsed, this);
37766 r.on("expanded", this.onRegionExpanded, this);
37770 * Performs a layout update.
37772 layout : function()
37774 if(this.updating) {
37778 // render all the rebions if they have not been done alreayd?
37779 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37780 if(this.regions[region] && !this.regions[region].bodyEl){
37781 this.regions[region].onRender(this.el)
37785 var size = this.getViewSize();
37786 var w = size.width;
37787 var h = size.height;
37792 //var x = 0, y = 0;
37794 var rs = this.regions;
37795 var north = rs["north"];
37796 var south = rs["south"];
37797 var west = rs["west"];
37798 var east = rs["east"];
37799 var center = rs["center"];
37800 //if(this.hideOnLayout){ // not supported anymore
37801 //c.el.setStyle("display", "none");
37803 if(north && north.isVisible()){
37804 var b = north.getBox();
37805 var m = north.getMargins();
37806 b.width = w - (m.left+m.right);
37809 centerY = b.height + b.y + m.bottom;
37810 centerH -= centerY;
37811 north.updateBox(this.safeBox(b));
37813 if(south && south.isVisible()){
37814 var b = south.getBox();
37815 var m = south.getMargins();
37816 b.width = w - (m.left+m.right);
37818 var totalHeight = (b.height + m.top + m.bottom);
37819 b.y = h - totalHeight + m.top;
37820 centerH -= totalHeight;
37821 south.updateBox(this.safeBox(b));
37823 if(west && west.isVisible()){
37824 var b = west.getBox();
37825 var m = west.getMargins();
37826 b.height = centerH - (m.top+m.bottom);
37828 b.y = centerY + m.top;
37829 var totalWidth = (b.width + m.left + m.right);
37830 centerX += totalWidth;
37831 centerW -= totalWidth;
37832 west.updateBox(this.safeBox(b));
37834 if(east && east.isVisible()){
37835 var b = east.getBox();
37836 var m = east.getMargins();
37837 b.height = centerH - (m.top+m.bottom);
37838 var totalWidth = (b.width + m.left + m.right);
37839 b.x = w - totalWidth + m.left;
37840 b.y = centerY + m.top;
37841 centerW -= totalWidth;
37842 east.updateBox(this.safeBox(b));
37845 var m = center.getMargins();
37847 x: centerX + m.left,
37848 y: centerY + m.top,
37849 width: centerW - (m.left+m.right),
37850 height: centerH - (m.top+m.bottom)
37852 //if(this.hideOnLayout){
37853 //center.el.setStyle("display", "block");
37855 center.updateBox(this.safeBox(centerBox));
37858 this.fireEvent("layout", this);
37862 safeBox : function(box){
37863 box.width = Math.max(0, box.width);
37864 box.height = Math.max(0, box.height);
37869 * Adds a ContentPanel (or subclass) to this layout.
37870 * @param {String} target The target region key (north, south, east, west or center).
37871 * @param {Roo.ContentPanel} panel The panel to add
37872 * @return {Roo.ContentPanel} The added panel
37874 add : function(target, panel){
37876 target = target.toLowerCase();
37877 return this.regions[target].add(panel);
37881 * Remove a ContentPanel (or subclass) to this layout.
37882 * @param {String} target The target region key (north, south, east, west or center).
37883 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37884 * @return {Roo.ContentPanel} The removed panel
37886 remove : function(target, panel){
37887 target = target.toLowerCase();
37888 return this.regions[target].remove(panel);
37892 * Searches all regions for a panel with the specified id
37893 * @param {String} panelId
37894 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37896 findPanel : function(panelId){
37897 var rs = this.regions;
37898 for(var target in rs){
37899 if(typeof rs[target] != "function"){
37900 var p = rs[target].getPanel(panelId);
37910 * Searches all regions for a panel with the specified id and activates (shows) it.
37911 * @param {String/ContentPanel} panelId The panels id or the panel itself
37912 * @return {Roo.ContentPanel} The shown panel or null
37914 showPanel : function(panelId) {
37915 var rs = this.regions;
37916 for(var target in rs){
37917 var r = rs[target];
37918 if(typeof r != "function"){
37919 if(r.hasPanel(panelId)){
37920 return r.showPanel(panelId);
37928 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37929 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37932 restoreState : function(provider){
37934 provider = Roo.state.Manager;
37936 var sm = new Roo.LayoutStateManager();
37937 sm.init(this, provider);
37943 * Adds a xtype elements to the layout.
37947 xtype : 'ContentPanel',
37954 xtype : 'NestedLayoutPanel',
37960 items : [ ... list of content panels or nested layout panels.. ]
37964 * @param {Object} cfg Xtype definition of item to add.
37966 addxtype : function(cfg)
37968 // basically accepts a pannel...
37969 // can accept a layout region..!?!?
37970 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37973 // theory? children can only be panels??
37975 //if (!cfg.xtype.match(/Panel$/)) {
37980 if (typeof(cfg.region) == 'undefined') {
37981 Roo.log("Failed to add Panel, region was not set");
37985 var region = cfg.region;
37991 xitems = cfg.items;
37996 if ( region == 'center') {
37997 Roo.log("Center: " + cfg.title);
38003 case 'Content': // ContentPanel (el, cfg)
38004 case 'Scroll': // ContentPanel (el, cfg)
38006 cfg.autoCreate = cfg.autoCreate || true;
38007 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38009 // var el = this.el.createChild();
38010 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38013 this.add(region, ret);
38017 case 'TreePanel': // our new panel!
38018 cfg.el = this.el.createChild();
38019 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38020 this.add(region, ret);
38025 // create a new Layout (which is a Border Layout...
38027 var clayout = cfg.layout;
38028 clayout.el = this.el.createChild();
38029 clayout.items = clayout.items || [];
38033 // replace this exitems with the clayout ones..
38034 xitems = clayout.items;
38036 // force background off if it's in center...
38037 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38038 cfg.background = false;
38040 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38043 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38044 //console.log('adding nested layout panel ' + cfg.toSource());
38045 this.add(region, ret);
38046 nb = {}; /// find first...
38051 // needs grid and region
38053 //var el = this.getRegion(region).el.createChild();
38055 *var el = this.el.createChild();
38056 // create the grid first...
38057 cfg.grid.container = el;
38058 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38061 if (region == 'center' && this.active ) {
38062 cfg.background = false;
38065 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38067 this.add(region, ret);
38069 if (cfg.background) {
38070 // render grid on panel activation (if panel background)
38071 ret.on('activate', function(gp) {
38072 if (!gp.grid.rendered) {
38073 // gp.grid.render(el);
38077 // cfg.grid.render(el);
38083 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38084 // it was the old xcomponent building that caused this before.
38085 // espeically if border is the top element in the tree.
38095 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38097 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38098 this.add(region, ret);
38102 throw "Can not add '" + cfg.xtype + "' to Border";
38108 this.beginUpdate();
38112 Roo.each(xitems, function(i) {
38113 region = nb && i.region ? i.region : false;
38115 var add = ret.addxtype(i);
38118 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38119 if (!i.background) {
38120 abn[region] = nb[region] ;
38127 // make the last non-background panel active..
38128 //if (nb) { Roo.log(abn); }
38131 for(var r in abn) {
38132 region = this.getRegion(r);
38134 // tried using nb[r], but it does not work..
38136 region.showPanel(abn[r]);
38147 factory : function(cfg)
38150 var validRegions = Roo.bootstrap.layout.Border.regions;
38152 var target = cfg.region;
38155 var r = Roo.bootstrap.layout;
38159 return new r.North(cfg);
38161 return new r.South(cfg);
38163 return new r.East(cfg);
38165 return new r.West(cfg);
38167 return new r.Center(cfg);
38169 throw 'Layout region "'+target+'" not supported.';
38176 * Ext JS Library 1.1.1
38177 * Copyright(c) 2006-2007, Ext JS, LLC.
38179 * Originally Released Under LGPL - original licence link has changed is not relivant.
38182 * <script type="text/javascript">
38186 * @class Roo.bootstrap.layout.Basic
38187 * @extends Roo.util.Observable
38188 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38189 * and does not have a titlebar, tabs or any other features. All it does is size and position
38190 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38191 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38192 * @cfg {string} region the region that it inhabits..
38193 * @cfg {bool} skipConfig skip config?
38197 Roo.bootstrap.layout.Basic = function(config){
38199 this.mgr = config.mgr;
38201 this.position = config.region;
38203 var skipConfig = config.skipConfig;
38207 * @scope Roo.BasicLayoutRegion
38211 * @event beforeremove
38212 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38213 * @param {Roo.LayoutRegion} this
38214 * @param {Roo.ContentPanel} panel The panel
38215 * @param {Object} e The cancel event object
38217 "beforeremove" : true,
38219 * @event invalidated
38220 * Fires when the layout for this region is changed.
38221 * @param {Roo.LayoutRegion} this
38223 "invalidated" : true,
38225 * @event visibilitychange
38226 * Fires when this region is shown or hidden
38227 * @param {Roo.LayoutRegion} this
38228 * @param {Boolean} visibility true or false
38230 "visibilitychange" : true,
38232 * @event paneladded
38233 * Fires when a panel is added.
38234 * @param {Roo.LayoutRegion} this
38235 * @param {Roo.ContentPanel} panel The panel
38237 "paneladded" : true,
38239 * @event panelremoved
38240 * Fires when a panel is removed.
38241 * @param {Roo.LayoutRegion} this
38242 * @param {Roo.ContentPanel} panel The panel
38244 "panelremoved" : true,
38246 * @event beforecollapse
38247 * Fires when this region before collapse.
38248 * @param {Roo.LayoutRegion} this
38250 "beforecollapse" : true,
38253 * Fires when this region is collapsed.
38254 * @param {Roo.LayoutRegion} this
38256 "collapsed" : true,
38259 * Fires when this region is expanded.
38260 * @param {Roo.LayoutRegion} this
38265 * Fires when this region is slid into view.
38266 * @param {Roo.LayoutRegion} this
38268 "slideshow" : true,
38271 * Fires when this region slides out of view.
38272 * @param {Roo.LayoutRegion} this
38274 "slidehide" : true,
38276 * @event panelactivated
38277 * Fires when a panel is activated.
38278 * @param {Roo.LayoutRegion} this
38279 * @param {Roo.ContentPanel} panel The activated panel
38281 "panelactivated" : true,
38284 * Fires when the user resizes this region.
38285 * @param {Roo.LayoutRegion} this
38286 * @param {Number} newSize The new size (width for east/west, height for north/south)
38290 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38291 this.panels = new Roo.util.MixedCollection();
38292 this.panels.getKey = this.getPanelId.createDelegate(this);
38294 this.activePanel = null;
38295 // ensure listeners are added...
38297 if (config.listeners || config.events) {
38298 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38299 listeners : config.listeners || {},
38300 events : config.events || {}
38304 if(skipConfig !== true){
38305 this.applyConfig(config);
38309 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38311 getPanelId : function(p){
38315 applyConfig : function(config){
38316 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38317 this.config = config;
38322 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38323 * the width, for horizontal (north, south) the height.
38324 * @param {Number} newSize The new width or height
38326 resizeTo : function(newSize){
38327 var el = this.el ? this.el :
38328 (this.activePanel ? this.activePanel.getEl() : null);
38330 switch(this.position){
38333 el.setWidth(newSize);
38334 this.fireEvent("resized", this, newSize);
38338 el.setHeight(newSize);
38339 this.fireEvent("resized", this, newSize);
38345 getBox : function(){
38346 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38349 getMargins : function(){
38350 return this.margins;
38353 updateBox : function(box){
38355 var el = this.activePanel.getEl();
38356 el.dom.style.left = box.x + "px";
38357 el.dom.style.top = box.y + "px";
38358 this.activePanel.setSize(box.width, box.height);
38362 * Returns the container element for this region.
38363 * @return {Roo.Element}
38365 getEl : function(){
38366 return this.activePanel;
38370 * Returns true if this region is currently visible.
38371 * @return {Boolean}
38373 isVisible : function(){
38374 return this.activePanel ? true : false;
38377 setActivePanel : function(panel){
38378 panel = this.getPanel(panel);
38379 if(this.activePanel && this.activePanel != panel){
38380 this.activePanel.setActiveState(false);
38381 this.activePanel.getEl().setLeftTop(-10000,-10000);
38383 this.activePanel = panel;
38384 panel.setActiveState(true);
38386 panel.setSize(this.box.width, this.box.height);
38388 this.fireEvent("panelactivated", this, panel);
38389 this.fireEvent("invalidated");
38393 * Show the specified panel.
38394 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38395 * @return {Roo.ContentPanel} The shown panel or null
38397 showPanel : function(panel){
38398 panel = this.getPanel(panel);
38400 this.setActivePanel(panel);
38406 * Get the active panel for this region.
38407 * @return {Roo.ContentPanel} The active panel or null
38409 getActivePanel : function(){
38410 return this.activePanel;
38414 * Add the passed ContentPanel(s)
38415 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38416 * @return {Roo.ContentPanel} The panel added (if only one was added)
38418 add : function(panel){
38419 if(arguments.length > 1){
38420 for(var i = 0, len = arguments.length; i < len; i++) {
38421 this.add(arguments[i]);
38425 if(this.hasPanel(panel)){
38426 this.showPanel(panel);
38429 var el = panel.getEl();
38430 if(el.dom.parentNode != this.mgr.el.dom){
38431 this.mgr.el.dom.appendChild(el.dom);
38433 if(panel.setRegion){
38434 panel.setRegion(this);
38436 this.panels.add(panel);
38437 el.setStyle("position", "absolute");
38438 if(!panel.background){
38439 this.setActivePanel(panel);
38440 if(this.config.initialSize && this.panels.getCount()==1){
38441 this.resizeTo(this.config.initialSize);
38444 this.fireEvent("paneladded", this, panel);
38449 * Returns true if the panel is in this region.
38450 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38451 * @return {Boolean}
38453 hasPanel : function(panel){
38454 if(typeof panel == "object"){ // must be panel obj
38455 panel = panel.getId();
38457 return this.getPanel(panel) ? true : false;
38461 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38462 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38463 * @param {Boolean} preservePanel Overrides the config preservePanel option
38464 * @return {Roo.ContentPanel} The panel that was removed
38466 remove : function(panel, preservePanel){
38467 panel = this.getPanel(panel);
38472 this.fireEvent("beforeremove", this, panel, e);
38473 if(e.cancel === true){
38476 var panelId = panel.getId();
38477 this.panels.removeKey(panelId);
38482 * Returns the panel specified or null if it's not in this region.
38483 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38484 * @return {Roo.ContentPanel}
38486 getPanel : function(id){
38487 if(typeof id == "object"){ // must be panel obj
38490 return this.panels.get(id);
38494 * Returns this regions position (north/south/east/west/center).
38497 getPosition: function(){
38498 return this.position;
38502 * Ext JS Library 1.1.1
38503 * Copyright(c) 2006-2007, Ext JS, LLC.
38505 * Originally Released Under LGPL - original licence link has changed is not relivant.
38508 * <script type="text/javascript">
38512 * @class Roo.bootstrap.layout.Region
38513 * @extends Roo.bootstrap.layout.Basic
38514 * This class represents a region in a layout manager.
38516 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38517 * @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})
38518 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38519 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38520 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38521 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38522 * @cfg {String} title The title for the region (overrides panel titles)
38523 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38524 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38525 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38526 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38527 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38528 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38529 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38530 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38531 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38532 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38534 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38535 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38536 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38537 * @cfg {Number} width For East/West panels
38538 * @cfg {Number} height For North/South panels
38539 * @cfg {Boolean} split To show the splitter
38540 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38542 * @cfg {string} cls Extra CSS classes to add to region
38544 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38545 * @cfg {string} region the region that it inhabits..
38548 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38549 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38551 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38552 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38553 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38555 Roo.bootstrap.layout.Region = function(config)
38557 this.applyConfig(config);
38559 var mgr = config.mgr;
38560 var pos = config.region;
38561 config.skipConfig = true;
38562 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38565 this.onRender(mgr.el);
38568 this.visible = true;
38569 this.collapsed = false;
38570 this.unrendered_panels = [];
38573 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38575 position: '', // set by wrapper (eg. north/south etc..)
38576 unrendered_panels : null, // unrendered panels.
38578 tabPosition : false,
38580 mgr: false, // points to 'Border'
38583 createBody : function(){
38584 /** This region's body element
38585 * @type Roo.Element */
38586 this.bodyEl = this.el.createChild({
38588 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38592 onRender: function(ctr, pos)
38594 var dh = Roo.DomHelper;
38595 /** This region's container element
38596 * @type Roo.Element */
38597 this.el = dh.append(ctr.dom, {
38599 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38601 /** This region's title element
38602 * @type Roo.Element */
38604 this.titleEl = dh.append(this.el.dom, {
38606 unselectable: "on",
38607 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38609 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38610 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38614 this.titleEl.enableDisplayMode();
38615 /** This region's title text element
38616 * @type HTMLElement */
38617 this.titleTextEl = this.titleEl.dom.firstChild;
38618 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38620 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38621 this.closeBtn.enableDisplayMode();
38622 this.closeBtn.on("click", this.closeClicked, this);
38623 this.closeBtn.hide();
38625 this.createBody(this.config);
38626 if(this.config.hideWhenEmpty){
38628 this.on("paneladded", this.validateVisibility, this);
38629 this.on("panelremoved", this.validateVisibility, this);
38631 if(this.autoScroll){
38632 this.bodyEl.setStyle("overflow", "auto");
38634 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38636 //if(c.titlebar !== false){
38637 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38638 this.titleEl.hide();
38640 this.titleEl.show();
38641 if(this.config.title){
38642 this.titleTextEl.innerHTML = this.config.title;
38646 if(this.config.collapsed){
38647 this.collapse(true);
38649 if(this.config.hidden){
38653 if (this.unrendered_panels && this.unrendered_panels.length) {
38654 for (var i =0;i< this.unrendered_panels.length; i++) {
38655 this.add(this.unrendered_panels[i]);
38657 this.unrendered_panels = null;
38663 applyConfig : function(c)
38666 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38667 var dh = Roo.DomHelper;
38668 if(c.titlebar !== false){
38669 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38670 this.collapseBtn.on("click", this.collapse, this);
38671 this.collapseBtn.enableDisplayMode();
38673 if(c.showPin === true || this.showPin){
38674 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38675 this.stickBtn.enableDisplayMode();
38676 this.stickBtn.on("click", this.expand, this);
38677 this.stickBtn.hide();
38682 /** This region's collapsed element
38683 * @type Roo.Element */
38686 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38687 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38690 if(c.floatable !== false){
38691 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38692 this.collapsedEl.on("click", this.collapseClick, this);
38695 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38696 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38697 id: "message", unselectable: "on", style:{"float":"left"}});
38698 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38700 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38701 this.expandBtn.on("click", this.expand, this);
38705 if(this.collapseBtn){
38706 this.collapseBtn.setVisible(c.collapsible == true);
38709 this.cmargins = c.cmargins || this.cmargins ||
38710 (this.position == "west" || this.position == "east" ?
38711 {top: 0, left: 2, right:2, bottom: 0} :
38712 {top: 2, left: 0, right:0, bottom: 2});
38714 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38717 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38719 this.autoScroll = c.autoScroll || false;
38724 this.duration = c.duration || .30;
38725 this.slideDuration = c.slideDuration || .45;
38730 * Returns true if this region is currently visible.
38731 * @return {Boolean}
38733 isVisible : function(){
38734 return this.visible;
38738 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38739 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38741 //setCollapsedTitle : function(title){
38742 // title = title || " ";
38743 // if(this.collapsedTitleTextEl){
38744 // this.collapsedTitleTextEl.innerHTML = title;
38748 getBox : function(){
38750 // if(!this.collapsed){
38751 b = this.el.getBox(false, true);
38753 // b = this.collapsedEl.getBox(false, true);
38758 getMargins : function(){
38759 return this.margins;
38760 //return this.collapsed ? this.cmargins : this.margins;
38763 highlight : function(){
38764 this.el.addClass("x-layout-panel-dragover");
38767 unhighlight : function(){
38768 this.el.removeClass("x-layout-panel-dragover");
38771 updateBox : function(box)
38773 if (!this.bodyEl) {
38774 return; // not rendered yet..
38778 if(!this.collapsed){
38779 this.el.dom.style.left = box.x + "px";
38780 this.el.dom.style.top = box.y + "px";
38781 this.updateBody(box.width, box.height);
38783 this.collapsedEl.dom.style.left = box.x + "px";
38784 this.collapsedEl.dom.style.top = box.y + "px";
38785 this.collapsedEl.setSize(box.width, box.height);
38788 this.tabs.autoSizeTabs();
38792 updateBody : function(w, h)
38795 this.el.setWidth(w);
38796 w -= this.el.getBorderWidth("rl");
38797 if(this.config.adjustments){
38798 w += this.config.adjustments[0];
38801 if(h !== null && h > 0){
38802 this.el.setHeight(h);
38803 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38804 h -= this.el.getBorderWidth("tb");
38805 if(this.config.adjustments){
38806 h += this.config.adjustments[1];
38808 this.bodyEl.setHeight(h);
38810 h = this.tabs.syncHeight(h);
38813 if(this.panelSize){
38814 w = w !== null ? w : this.panelSize.width;
38815 h = h !== null ? h : this.panelSize.height;
38817 if(this.activePanel){
38818 var el = this.activePanel.getEl();
38819 w = w !== null ? w : el.getWidth();
38820 h = h !== null ? h : el.getHeight();
38821 this.panelSize = {width: w, height: h};
38822 this.activePanel.setSize(w, h);
38824 if(Roo.isIE && this.tabs){
38825 this.tabs.el.repaint();
38830 * Returns the container element for this region.
38831 * @return {Roo.Element}
38833 getEl : function(){
38838 * Hides this region.
38841 //if(!this.collapsed){
38842 this.el.dom.style.left = "-2000px";
38845 // this.collapsedEl.dom.style.left = "-2000px";
38846 // this.collapsedEl.hide();
38848 this.visible = false;
38849 this.fireEvent("visibilitychange", this, false);
38853 * Shows this region if it was previously hidden.
38856 //if(!this.collapsed){
38859 // this.collapsedEl.show();
38861 this.visible = true;
38862 this.fireEvent("visibilitychange", this, true);
38865 closeClicked : function(){
38866 if(this.activePanel){
38867 this.remove(this.activePanel);
38871 collapseClick : function(e){
38873 e.stopPropagation();
38876 e.stopPropagation();
38882 * Collapses this region.
38883 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38886 collapse : function(skipAnim, skipCheck = false){
38887 if(this.collapsed) {
38891 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38893 this.collapsed = true;
38895 this.split.el.hide();
38897 if(this.config.animate && skipAnim !== true){
38898 this.fireEvent("invalidated", this);
38899 this.animateCollapse();
38901 this.el.setLocation(-20000,-20000);
38903 this.collapsedEl.show();
38904 this.fireEvent("collapsed", this);
38905 this.fireEvent("invalidated", this);
38911 animateCollapse : function(){
38916 * Expands this region if it was previously collapsed.
38917 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38918 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38921 expand : function(e, skipAnim){
38923 e.stopPropagation();
38925 if(!this.collapsed || this.el.hasActiveFx()) {
38929 this.afterSlideIn();
38932 this.collapsed = false;
38933 if(this.config.animate && skipAnim !== true){
38934 this.animateExpand();
38938 this.split.el.show();
38940 this.collapsedEl.setLocation(-2000,-2000);
38941 this.collapsedEl.hide();
38942 this.fireEvent("invalidated", this);
38943 this.fireEvent("expanded", this);
38947 animateExpand : function(){
38951 initTabs : function()
38953 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38955 var ts = new Roo.bootstrap.panel.Tabs({
38956 el: this.bodyEl.dom,
38958 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38959 disableTooltips: this.config.disableTabTips,
38960 toolbar : this.config.toolbar
38963 if(this.config.hideTabs){
38964 ts.stripWrap.setDisplayed(false);
38967 ts.resizeTabs = this.config.resizeTabs === true;
38968 ts.minTabWidth = this.config.minTabWidth || 40;
38969 ts.maxTabWidth = this.config.maxTabWidth || 250;
38970 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38971 ts.monitorResize = false;
38972 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38973 ts.bodyEl.addClass('roo-layout-tabs-body');
38974 this.panels.each(this.initPanelAsTab, this);
38977 initPanelAsTab : function(panel){
38978 var ti = this.tabs.addTab(
38982 this.config.closeOnTab && panel.isClosable(),
38985 if(panel.tabTip !== undefined){
38986 ti.setTooltip(panel.tabTip);
38988 ti.on("activate", function(){
38989 this.setActivePanel(panel);
38992 if(this.config.closeOnTab){
38993 ti.on("beforeclose", function(t, e){
38995 this.remove(panel);
38999 panel.tabItem = ti;
39004 updatePanelTitle : function(panel, title)
39006 if(this.activePanel == panel){
39007 this.updateTitle(title);
39010 var ti = this.tabs.getTab(panel.getEl().id);
39012 if(panel.tabTip !== undefined){
39013 ti.setTooltip(panel.tabTip);
39018 updateTitle : function(title){
39019 if(this.titleTextEl && !this.config.title){
39020 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39024 setActivePanel : function(panel)
39026 panel = this.getPanel(panel);
39027 if(this.activePanel && this.activePanel != panel){
39028 if(this.activePanel.setActiveState(false) === false){
39032 this.activePanel = panel;
39033 panel.setActiveState(true);
39034 if(this.panelSize){
39035 panel.setSize(this.panelSize.width, this.panelSize.height);
39038 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39040 this.updateTitle(panel.getTitle());
39042 this.fireEvent("invalidated", this);
39044 this.fireEvent("panelactivated", this, panel);
39048 * Shows the specified panel.
39049 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39050 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39052 showPanel : function(panel)
39054 panel = this.getPanel(panel);
39057 var tab = this.tabs.getTab(panel.getEl().id);
39058 if(tab.isHidden()){
39059 this.tabs.unhideTab(tab.id);
39063 this.setActivePanel(panel);
39070 * Get the active panel for this region.
39071 * @return {Roo.ContentPanel} The active panel or null
39073 getActivePanel : function(){
39074 return this.activePanel;
39077 validateVisibility : function(){
39078 if(this.panels.getCount() < 1){
39079 this.updateTitle(" ");
39080 this.closeBtn.hide();
39083 if(!this.isVisible()){
39090 * Adds the passed ContentPanel(s) to this region.
39091 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39092 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39094 add : function(panel)
39096 if(arguments.length > 1){
39097 for(var i = 0, len = arguments.length; i < len; i++) {
39098 this.add(arguments[i]);
39103 // if we have not been rendered yet, then we can not really do much of this..
39104 if (!this.bodyEl) {
39105 this.unrendered_panels.push(panel);
39112 if(this.hasPanel(panel)){
39113 this.showPanel(panel);
39116 panel.setRegion(this);
39117 this.panels.add(panel);
39118 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39119 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39120 // and hide them... ???
39121 this.bodyEl.dom.appendChild(panel.getEl().dom);
39122 if(panel.background !== true){
39123 this.setActivePanel(panel);
39125 this.fireEvent("paneladded", this, panel);
39132 this.initPanelAsTab(panel);
39136 if(panel.background !== true){
39137 this.tabs.activate(panel.getEl().id);
39139 this.fireEvent("paneladded", this, panel);
39144 * Hides the tab for the specified panel.
39145 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39147 hidePanel : function(panel){
39148 if(this.tabs && (panel = this.getPanel(panel))){
39149 this.tabs.hideTab(panel.getEl().id);
39154 * Unhides the tab for a previously hidden panel.
39155 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39157 unhidePanel : function(panel){
39158 if(this.tabs && (panel = this.getPanel(panel))){
39159 this.tabs.unhideTab(panel.getEl().id);
39163 clearPanels : function(){
39164 while(this.panels.getCount() > 0){
39165 this.remove(this.panels.first());
39170 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39171 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39172 * @param {Boolean} preservePanel Overrides the config preservePanel option
39173 * @return {Roo.ContentPanel} The panel that was removed
39175 remove : function(panel, preservePanel)
39177 panel = this.getPanel(panel);
39182 this.fireEvent("beforeremove", this, panel, e);
39183 if(e.cancel === true){
39186 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39187 var panelId = panel.getId();
39188 this.panels.removeKey(panelId);
39190 document.body.appendChild(panel.getEl().dom);
39193 this.tabs.removeTab(panel.getEl().id);
39194 }else if (!preservePanel){
39195 this.bodyEl.dom.removeChild(panel.getEl().dom);
39197 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39198 var p = this.panels.first();
39199 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39200 tempEl.appendChild(p.getEl().dom);
39201 this.bodyEl.update("");
39202 this.bodyEl.dom.appendChild(p.getEl().dom);
39204 this.updateTitle(p.getTitle());
39206 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39207 this.setActivePanel(p);
39209 panel.setRegion(null);
39210 if(this.activePanel == panel){
39211 this.activePanel = null;
39213 if(this.config.autoDestroy !== false && preservePanel !== true){
39214 try{panel.destroy();}catch(e){}
39216 this.fireEvent("panelremoved", this, panel);
39221 * Returns the TabPanel component used by this region
39222 * @return {Roo.TabPanel}
39224 getTabs : function(){
39228 createTool : function(parentEl, className){
39229 var btn = Roo.DomHelper.append(parentEl, {
39231 cls: "x-layout-tools-button",
39234 cls: "roo-layout-tools-button-inner " + className,
39238 btn.addClassOnOver("roo-layout-tools-button-over");
39243 * Ext JS Library 1.1.1
39244 * Copyright(c) 2006-2007, Ext JS, LLC.
39246 * Originally Released Under LGPL - original licence link has changed is not relivant.
39249 * <script type="text/javascript">
39255 * @class Roo.SplitLayoutRegion
39256 * @extends Roo.LayoutRegion
39257 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39259 Roo.bootstrap.layout.Split = function(config){
39260 this.cursor = config.cursor;
39261 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39264 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39266 splitTip : "Drag to resize.",
39267 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39268 useSplitTips : false,
39270 applyConfig : function(config){
39271 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39274 onRender : function(ctr,pos) {
39276 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39277 if(!this.config.split){
39282 var splitEl = Roo.DomHelper.append(ctr.dom, {
39284 id: this.el.id + "-split",
39285 cls: "roo-layout-split roo-layout-split-"+this.position,
39288 /** The SplitBar for this region
39289 * @type Roo.SplitBar */
39290 // does not exist yet...
39291 Roo.log([this.position, this.orientation]);
39293 this.split = new Roo.bootstrap.SplitBar({
39294 dragElement : splitEl,
39295 resizingElement: this.el,
39296 orientation : this.orientation
39299 this.split.on("moved", this.onSplitMove, this);
39300 this.split.useShim = this.config.useShim === true;
39301 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39302 if(this.useSplitTips){
39303 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39305 //if(config.collapsible){
39306 // this.split.el.on("dblclick", this.collapse, this);
39309 if(typeof this.config.minSize != "undefined"){
39310 this.split.minSize = this.config.minSize;
39312 if(typeof this.config.maxSize != "undefined"){
39313 this.split.maxSize = this.config.maxSize;
39315 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39316 this.hideSplitter();
39321 getHMaxSize : function(){
39322 var cmax = this.config.maxSize || 10000;
39323 var center = this.mgr.getRegion("center");
39324 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39327 getVMaxSize : function(){
39328 var cmax = this.config.maxSize || 10000;
39329 var center = this.mgr.getRegion("center");
39330 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39333 onSplitMove : function(split, newSize){
39334 this.fireEvent("resized", this, newSize);
39338 * Returns the {@link Roo.SplitBar} for this region.
39339 * @return {Roo.SplitBar}
39341 getSplitBar : function(){
39346 this.hideSplitter();
39347 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39350 hideSplitter : function(){
39352 this.split.el.setLocation(-2000,-2000);
39353 this.split.el.hide();
39359 this.split.el.show();
39361 Roo.bootstrap.layout.Split.superclass.show.call(this);
39364 beforeSlide: function(){
39365 if(Roo.isGecko){// firefox overflow auto bug workaround
39366 this.bodyEl.clip();
39368 this.tabs.bodyEl.clip();
39370 if(this.activePanel){
39371 this.activePanel.getEl().clip();
39373 if(this.activePanel.beforeSlide){
39374 this.activePanel.beforeSlide();
39380 afterSlide : function(){
39381 if(Roo.isGecko){// firefox overflow auto bug workaround
39382 this.bodyEl.unclip();
39384 this.tabs.bodyEl.unclip();
39386 if(this.activePanel){
39387 this.activePanel.getEl().unclip();
39388 if(this.activePanel.afterSlide){
39389 this.activePanel.afterSlide();
39395 initAutoHide : function(){
39396 if(this.autoHide !== false){
39397 if(!this.autoHideHd){
39398 var st = new Roo.util.DelayedTask(this.slideIn, this);
39399 this.autoHideHd = {
39400 "mouseout": function(e){
39401 if(!e.within(this.el, true)){
39405 "mouseover" : function(e){
39411 this.el.on(this.autoHideHd);
39415 clearAutoHide : function(){
39416 if(this.autoHide !== false){
39417 this.el.un("mouseout", this.autoHideHd.mouseout);
39418 this.el.un("mouseover", this.autoHideHd.mouseover);
39422 clearMonitor : function(){
39423 Roo.get(document).un("click", this.slideInIf, this);
39426 // these names are backwards but not changed for compat
39427 slideOut : function(){
39428 if(this.isSlid || this.el.hasActiveFx()){
39431 this.isSlid = true;
39432 if(this.collapseBtn){
39433 this.collapseBtn.hide();
39435 this.closeBtnState = this.closeBtn.getStyle('display');
39436 this.closeBtn.hide();
39438 this.stickBtn.show();
39441 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39442 this.beforeSlide();
39443 this.el.setStyle("z-index", 10001);
39444 this.el.slideIn(this.getSlideAnchor(), {
39445 callback: function(){
39447 this.initAutoHide();
39448 Roo.get(document).on("click", this.slideInIf, this);
39449 this.fireEvent("slideshow", this);
39456 afterSlideIn : function(){
39457 this.clearAutoHide();
39458 this.isSlid = false;
39459 this.clearMonitor();
39460 this.el.setStyle("z-index", "");
39461 if(this.collapseBtn){
39462 this.collapseBtn.show();
39464 this.closeBtn.setStyle('display', this.closeBtnState);
39466 this.stickBtn.hide();
39468 this.fireEvent("slidehide", this);
39471 slideIn : function(cb){
39472 if(!this.isSlid || this.el.hasActiveFx()){
39476 this.isSlid = false;
39477 this.beforeSlide();
39478 this.el.slideOut(this.getSlideAnchor(), {
39479 callback: function(){
39480 this.el.setLeftTop(-10000, -10000);
39482 this.afterSlideIn();
39490 slideInIf : function(e){
39491 if(!e.within(this.el)){
39496 animateCollapse : function(){
39497 this.beforeSlide();
39498 this.el.setStyle("z-index", 20000);
39499 var anchor = this.getSlideAnchor();
39500 this.el.slideOut(anchor, {
39501 callback : function(){
39502 this.el.setStyle("z-index", "");
39503 this.collapsedEl.slideIn(anchor, {duration:.3});
39505 this.el.setLocation(-10000,-10000);
39507 this.fireEvent("collapsed", this);
39514 animateExpand : function(){
39515 this.beforeSlide();
39516 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39517 this.el.setStyle("z-index", 20000);
39518 this.collapsedEl.hide({
39521 this.el.slideIn(this.getSlideAnchor(), {
39522 callback : function(){
39523 this.el.setStyle("z-index", "");
39526 this.split.el.show();
39528 this.fireEvent("invalidated", this);
39529 this.fireEvent("expanded", this);
39557 getAnchor : function(){
39558 return this.anchors[this.position];
39561 getCollapseAnchor : function(){
39562 return this.canchors[this.position];
39565 getSlideAnchor : function(){
39566 return this.sanchors[this.position];
39569 getAlignAdj : function(){
39570 var cm = this.cmargins;
39571 switch(this.position){
39587 getExpandAdj : function(){
39588 var c = this.collapsedEl, cm = this.cmargins;
39589 switch(this.position){
39591 return [-(cm.right+c.getWidth()+cm.left), 0];
39594 return [cm.right+c.getWidth()+cm.left, 0];
39597 return [0, -(cm.top+cm.bottom+c.getHeight())];
39600 return [0, cm.top+cm.bottom+c.getHeight()];
39606 * Ext JS Library 1.1.1
39607 * Copyright(c) 2006-2007, Ext JS, LLC.
39609 * Originally Released Under LGPL - original licence link has changed is not relivant.
39612 * <script type="text/javascript">
39615 * These classes are private internal classes
39617 Roo.bootstrap.layout.Center = function(config){
39618 config.region = "center";
39619 Roo.bootstrap.layout.Region.call(this, config);
39620 this.visible = true;
39621 this.minWidth = config.minWidth || 20;
39622 this.minHeight = config.minHeight || 20;
39625 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39627 // center panel can't be hidden
39631 // center panel can't be hidden
39634 getMinWidth: function(){
39635 return this.minWidth;
39638 getMinHeight: function(){
39639 return this.minHeight;
39653 Roo.bootstrap.layout.North = function(config)
39655 config.region = 'north';
39656 config.cursor = 'n-resize';
39658 Roo.bootstrap.layout.Split.call(this, config);
39662 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39663 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39664 this.split.el.addClass("roo-layout-split-v");
39666 //var size = config.initialSize || config.height;
39667 //if(this.el && typeof size != "undefined"){
39668 // this.el.setHeight(size);
39671 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39673 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39676 onRender : function(ctr, pos)
39678 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39679 var size = this.config.initialSize || this.config.height;
39680 if(this.el && typeof size != "undefined"){
39681 this.el.setHeight(size);
39686 getBox : function(){
39687 if(this.collapsed){
39688 return this.collapsedEl.getBox();
39690 var box = this.el.getBox();
39692 box.height += this.split.el.getHeight();
39697 updateBox : function(box){
39698 if(this.split && !this.collapsed){
39699 box.height -= this.split.el.getHeight();
39700 this.split.el.setLeft(box.x);
39701 this.split.el.setTop(box.y+box.height);
39702 this.split.el.setWidth(box.width);
39704 if(this.collapsed){
39705 this.updateBody(box.width, null);
39707 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39715 Roo.bootstrap.layout.South = function(config){
39716 config.region = 'south';
39717 config.cursor = 's-resize';
39718 Roo.bootstrap.layout.Split.call(this, config);
39720 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39721 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39722 this.split.el.addClass("roo-layout-split-v");
39727 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39728 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39730 onRender : function(ctr, pos)
39732 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39733 var size = this.config.initialSize || this.config.height;
39734 if(this.el && typeof size != "undefined"){
39735 this.el.setHeight(size);
39740 getBox : function(){
39741 if(this.collapsed){
39742 return this.collapsedEl.getBox();
39744 var box = this.el.getBox();
39746 var sh = this.split.el.getHeight();
39753 updateBox : function(box){
39754 if(this.split && !this.collapsed){
39755 var sh = this.split.el.getHeight();
39758 this.split.el.setLeft(box.x);
39759 this.split.el.setTop(box.y-sh);
39760 this.split.el.setWidth(box.width);
39762 if(this.collapsed){
39763 this.updateBody(box.width, null);
39765 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39769 Roo.bootstrap.layout.East = function(config){
39770 config.region = "east";
39771 config.cursor = "e-resize";
39772 Roo.bootstrap.layout.Split.call(this, config);
39774 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39775 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39776 this.split.el.addClass("roo-layout-split-h");
39780 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39781 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39783 onRender : function(ctr, pos)
39785 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39786 var size = this.config.initialSize || this.config.width;
39787 if(this.el && typeof size != "undefined"){
39788 this.el.setWidth(size);
39793 getBox : function(){
39794 if(this.collapsed){
39795 return this.collapsedEl.getBox();
39797 var box = this.el.getBox();
39799 var sw = this.split.el.getWidth();
39806 updateBox : function(box){
39807 if(this.split && !this.collapsed){
39808 var sw = this.split.el.getWidth();
39810 this.split.el.setLeft(box.x);
39811 this.split.el.setTop(box.y);
39812 this.split.el.setHeight(box.height);
39815 if(this.collapsed){
39816 this.updateBody(null, box.height);
39818 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39822 Roo.bootstrap.layout.West = function(config){
39823 config.region = "west";
39824 config.cursor = "w-resize";
39826 Roo.bootstrap.layout.Split.call(this, config);
39828 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39829 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39830 this.split.el.addClass("roo-layout-split-h");
39834 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39835 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39837 onRender: function(ctr, pos)
39839 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39840 var size = this.config.initialSize || this.config.width;
39841 if(typeof size != "undefined"){
39842 this.el.setWidth(size);
39846 getBox : function(){
39847 if(this.collapsed){
39848 return this.collapsedEl.getBox();
39850 var box = this.el.getBox();
39851 if (box.width == 0) {
39852 box.width = this.config.width; // kludge?
39855 box.width += this.split.el.getWidth();
39860 updateBox : function(box){
39861 if(this.split && !this.collapsed){
39862 var sw = this.split.el.getWidth();
39864 this.split.el.setLeft(box.x+box.width);
39865 this.split.el.setTop(box.y);
39866 this.split.el.setHeight(box.height);
39868 if(this.collapsed){
39869 this.updateBody(null, box.height);
39871 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39873 });Roo.namespace("Roo.bootstrap.panel");/*
39875 * Ext JS Library 1.1.1
39876 * Copyright(c) 2006-2007, Ext JS, LLC.
39878 * Originally Released Under LGPL - original licence link has changed is not relivant.
39881 * <script type="text/javascript">
39884 * @class Roo.ContentPanel
39885 * @extends Roo.util.Observable
39886 * A basic ContentPanel element.
39887 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39888 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39889 * @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
39890 * @cfg {Boolean} closable True if the panel can be closed/removed
39891 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39892 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39893 * @cfg {Toolbar} toolbar A toolbar for this panel
39894 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39895 * @cfg {String} title The title for this panel
39896 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39897 * @cfg {String} url Calls {@link #setUrl} with this value
39898 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39899 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39900 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39901 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39902 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39903 * @cfg {Boolean} badges render the badges
39904 * @cfg {String} cls extra classes to use
39905 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39908 * Create a new ContentPanel.
39909 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39910 * @param {String/Object} config A string to set only the title or a config object
39911 * @param {String} content (optional) Set the HTML content for this panel
39912 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39914 Roo.bootstrap.panel.Content = function( config){
39916 this.tpl = config.tpl || false;
39918 var el = config.el;
39919 var content = config.content;
39921 if(config.autoCreate){ // xtype is available if this is called from factory
39924 this.el = Roo.get(el);
39925 if(!this.el && config && config.autoCreate){
39926 if(typeof config.autoCreate == "object"){
39927 if(!config.autoCreate.id){
39928 config.autoCreate.id = config.id||el;
39930 this.el = Roo.DomHelper.append(document.body,
39931 config.autoCreate, true);
39935 cls: (config.cls || '') +
39936 (config.background ? ' bg-' + config.background : '') +
39937 " roo-layout-inactive-content",
39940 if (config.iframe) {
39944 style : 'border: 0px',
39945 src : 'about:blank'
39951 elcfg.html = config.html;
39955 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39956 if (config.iframe) {
39957 this.iframeEl = this.el.select('iframe',true).first();
39962 this.closable = false;
39963 this.loaded = false;
39964 this.active = false;
39967 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39969 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39971 this.wrapEl = this.el; //this.el.wrap();
39973 if (config.toolbar.items) {
39974 ti = config.toolbar.items ;
39975 delete config.toolbar.items ;
39979 this.toolbar.render(this.wrapEl, 'before');
39980 for(var i =0;i < ti.length;i++) {
39981 // Roo.log(['add child', items[i]]);
39982 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39984 this.toolbar.items = nitems;
39985 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39986 delete config.toolbar;
39990 // xtype created footer. - not sure if will work as we normally have to render first..
39991 if (this.footer && !this.footer.el && this.footer.xtype) {
39992 if (!this.wrapEl) {
39993 this.wrapEl = this.el.wrap();
39996 this.footer.container = this.wrapEl.createChild();
39998 this.footer = Roo.factory(this.footer, Roo);
40003 if(typeof config == "string"){
40004 this.title = config;
40006 Roo.apply(this, config);
40010 this.resizeEl = Roo.get(this.resizeEl, true);
40012 this.resizeEl = this.el;
40014 // handle view.xtype
40022 * Fires when this panel is activated.
40023 * @param {Roo.ContentPanel} this
40027 * @event deactivate
40028 * Fires when this panel is activated.
40029 * @param {Roo.ContentPanel} this
40031 "deactivate" : true,
40035 * Fires when this panel is resized if fitToFrame is true.
40036 * @param {Roo.ContentPanel} this
40037 * @param {Number} width The width after any component adjustments
40038 * @param {Number} height The height after any component adjustments
40044 * Fires when this tab is created
40045 * @param {Roo.ContentPanel} this
40056 if(this.autoScroll && !this.iframe){
40057 this.resizeEl.setStyle("overflow", "auto");
40059 // fix randome scrolling
40060 //this.el.on('scroll', function() {
40061 // Roo.log('fix random scolling');
40062 // this.scrollTo('top',0);
40065 content = content || this.content;
40067 this.setContent(content);
40069 if(config && config.url){
40070 this.setUrl(this.url, this.params, this.loadOnce);
40075 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40077 if (this.view && typeof(this.view.xtype) != 'undefined') {
40078 this.view.el = this.el.appendChild(document.createElement("div"));
40079 this.view = Roo.factory(this.view);
40080 this.view.render && this.view.render(false, '');
40084 this.fireEvent('render', this);
40087 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40097 setRegion : function(region){
40098 this.region = region;
40099 this.setActiveClass(region && !this.background);
40103 setActiveClass: function(state)
40106 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40107 this.el.setStyle('position','relative');
40109 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40110 this.el.setStyle('position', 'absolute');
40115 * Returns the toolbar for this Panel if one was configured.
40116 * @return {Roo.Toolbar}
40118 getToolbar : function(){
40119 return this.toolbar;
40122 setActiveState : function(active)
40124 this.active = active;
40125 this.setActiveClass(active);
40127 if(this.fireEvent("deactivate", this) === false){
40132 this.fireEvent("activate", this);
40136 * Updates this panel's element (not for iframe)
40137 * @param {String} content The new content
40138 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40140 setContent : function(content, loadScripts){
40145 this.el.update(content, loadScripts);
40148 ignoreResize : function(w, h){
40149 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40152 this.lastSize = {width: w, height: h};
40157 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40158 * @return {Roo.UpdateManager} The UpdateManager
40160 getUpdateManager : function(){
40164 return this.el.getUpdateManager();
40167 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40168 * Does not work with IFRAME contents
40169 * @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:
40172 url: "your-url.php",
40173 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40174 callback: yourFunction,
40175 scope: yourObject, //(optional scope)
40178 text: "Loading...",
40184 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40185 * 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.
40186 * @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}
40187 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40188 * @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.
40189 * @return {Roo.ContentPanel} this
40197 var um = this.el.getUpdateManager();
40198 um.update.apply(um, arguments);
40204 * 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.
40205 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40206 * @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)
40207 * @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)
40208 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40210 setUrl : function(url, params, loadOnce){
40212 this.iframeEl.dom.src = url;
40216 if(this.refreshDelegate){
40217 this.removeListener("activate", this.refreshDelegate);
40219 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40220 this.on("activate", this.refreshDelegate);
40221 return this.el.getUpdateManager();
40224 _handleRefresh : function(url, params, loadOnce){
40225 if(!loadOnce || !this.loaded){
40226 var updater = this.el.getUpdateManager();
40227 updater.update(url, params, this._setLoaded.createDelegate(this));
40231 _setLoaded : function(){
40232 this.loaded = true;
40236 * Returns this panel's id
40239 getId : function(){
40244 * Returns this panel's element - used by regiosn to add.
40245 * @return {Roo.Element}
40247 getEl : function(){
40248 return this.wrapEl || this.el;
40253 adjustForComponents : function(width, height)
40255 //Roo.log('adjustForComponents ');
40256 if(this.resizeEl != this.el){
40257 width -= this.el.getFrameWidth('lr');
40258 height -= this.el.getFrameWidth('tb');
40261 var te = this.toolbar.getEl();
40262 te.setWidth(width);
40263 height -= te.getHeight();
40266 var te = this.footer.getEl();
40267 te.setWidth(width);
40268 height -= te.getHeight();
40272 if(this.adjustments){
40273 width += this.adjustments[0];
40274 height += this.adjustments[1];
40276 return {"width": width, "height": height};
40279 setSize : function(width, height){
40280 if(this.fitToFrame && !this.ignoreResize(width, height)){
40281 if(this.fitContainer && this.resizeEl != this.el){
40282 this.el.setSize(width, height);
40284 var size = this.adjustForComponents(width, height);
40286 this.iframeEl.setSize(width,height);
40289 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40290 this.fireEvent('resize', this, size.width, size.height);
40297 * Returns this panel's title
40300 getTitle : function(){
40302 if (typeof(this.title) != 'object') {
40307 for (var k in this.title) {
40308 if (!this.title.hasOwnProperty(k)) {
40312 if (k.indexOf('-') >= 0) {
40313 var s = k.split('-');
40314 for (var i = 0; i<s.length; i++) {
40315 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40318 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40325 * Set this panel's title
40326 * @param {String} title
40328 setTitle : function(title){
40329 this.title = title;
40331 this.region.updatePanelTitle(this, title);
40336 * Returns true is this panel was configured to be closable
40337 * @return {Boolean}
40339 isClosable : function(){
40340 return this.closable;
40343 beforeSlide : function(){
40345 this.resizeEl.clip();
40348 afterSlide : function(){
40350 this.resizeEl.unclip();
40354 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40355 * Will fail silently if the {@link #setUrl} method has not been called.
40356 * This does not activate the panel, just updates its content.
40358 refresh : function(){
40359 if(this.refreshDelegate){
40360 this.loaded = false;
40361 this.refreshDelegate();
40366 * Destroys this panel
40368 destroy : function(){
40369 this.el.removeAllListeners();
40370 var tempEl = document.createElement("span");
40371 tempEl.appendChild(this.el.dom);
40372 tempEl.innerHTML = "";
40378 * form - if the content panel contains a form - this is a reference to it.
40379 * @type {Roo.form.Form}
40383 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40384 * This contains a reference to it.
40390 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40400 * @param {Object} cfg Xtype definition of item to add.
40404 getChildContainer: function () {
40405 return this.getEl();
40410 var ret = new Roo.factory(cfg);
40415 if (cfg.xtype.match(/^Form$/)) {
40418 //if (this.footer) {
40419 // el = this.footer.container.insertSibling(false, 'before');
40421 el = this.el.createChild();
40424 this.form = new Roo.form.Form(cfg);
40427 if ( this.form.allItems.length) {
40428 this.form.render(el.dom);
40432 // should only have one of theses..
40433 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40434 // views.. should not be just added - used named prop 'view''
40436 cfg.el = this.el.appendChild(document.createElement("div"));
40439 var ret = new Roo.factory(cfg);
40441 ret.render && ret.render(false, ''); // render blank..
40451 * @class Roo.bootstrap.panel.Grid
40452 * @extends Roo.bootstrap.panel.Content
40454 * Create a new GridPanel.
40455 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40456 * @param {Object} config A the config object
40462 Roo.bootstrap.panel.Grid = function(config)
40466 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40467 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40469 config.el = this.wrapper;
40470 //this.el = this.wrapper;
40472 if (config.container) {
40473 // ctor'ed from a Border/panel.grid
40476 this.wrapper.setStyle("overflow", "hidden");
40477 this.wrapper.addClass('roo-grid-container');
40482 if(config.toolbar){
40483 var tool_el = this.wrapper.createChild();
40484 this.toolbar = Roo.factory(config.toolbar);
40486 if (config.toolbar.items) {
40487 ti = config.toolbar.items ;
40488 delete config.toolbar.items ;
40492 this.toolbar.render(tool_el);
40493 for(var i =0;i < ti.length;i++) {
40494 // Roo.log(['add child', items[i]]);
40495 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40497 this.toolbar.items = nitems;
40499 delete config.toolbar;
40502 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40503 config.grid.scrollBody = true;;
40504 config.grid.monitorWindowResize = false; // turn off autosizing
40505 config.grid.autoHeight = false;
40506 config.grid.autoWidth = false;
40508 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40510 if (config.background) {
40511 // render grid on panel activation (if panel background)
40512 this.on('activate', function(gp) {
40513 if (!gp.grid.rendered) {
40514 gp.grid.render(this.wrapper);
40515 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40520 this.grid.render(this.wrapper);
40521 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40524 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40525 // ??? needed ??? config.el = this.wrapper;
40530 // xtype created footer. - not sure if will work as we normally have to render first..
40531 if (this.footer && !this.footer.el && this.footer.xtype) {
40533 var ctr = this.grid.getView().getFooterPanel(true);
40534 this.footer.dataSource = this.grid.dataSource;
40535 this.footer = Roo.factory(this.footer, Roo);
40536 this.footer.render(ctr);
40546 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40547 getId : function(){
40548 return this.grid.id;
40552 * Returns the grid for this panel
40553 * @return {Roo.bootstrap.Table}
40555 getGrid : function(){
40559 setSize : function(width, height){
40560 if(!this.ignoreResize(width, height)){
40561 var grid = this.grid;
40562 var size = this.adjustForComponents(width, height);
40563 // tfoot is not a footer?
40566 var gridel = grid.getGridEl();
40567 gridel.setSize(size.width, size.height);
40569 var tbd = grid.getGridEl().select('tbody', true).first();
40570 var thd = grid.getGridEl().select('thead',true).first();
40571 var tbf= grid.getGridEl().select('tfoot', true).first();
40574 size.height -= tbf.getHeight();
40577 size.height -= thd.getHeight();
40580 tbd.setSize(size.width, size.height );
40581 // this is for the account management tab -seems to work there.
40582 var thd = grid.getGridEl().select('thead',true).first();
40584 // tbd.setSize(size.width, size.height - thd.getHeight());
40593 beforeSlide : function(){
40594 this.grid.getView().scroller.clip();
40597 afterSlide : function(){
40598 this.grid.getView().scroller.unclip();
40601 destroy : function(){
40602 this.grid.destroy();
40604 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40609 * @class Roo.bootstrap.panel.Nest
40610 * @extends Roo.bootstrap.panel.Content
40612 * Create a new Panel, that can contain a layout.Border.
40615 * @param {Roo.BorderLayout} layout The layout for this panel
40616 * @param {String/Object} config A string to set only the title or a config object
40618 Roo.bootstrap.panel.Nest = function(config)
40620 // construct with only one argument..
40621 /* FIXME - implement nicer consturctors
40622 if (layout.layout) {
40624 layout = config.layout;
40625 delete config.layout;
40627 if (layout.xtype && !layout.getEl) {
40628 // then layout needs constructing..
40629 layout = Roo.factory(layout, Roo);
40633 config.el = config.layout.getEl();
40635 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40637 config.layout.monitorWindowResize = false; // turn off autosizing
40638 this.layout = config.layout;
40639 this.layout.getEl().addClass("roo-layout-nested-layout");
40640 this.layout.parent = this;
40647 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40649 setSize : function(width, height){
40650 if(!this.ignoreResize(width, height)){
40651 var size = this.adjustForComponents(width, height);
40652 var el = this.layout.getEl();
40653 if (size.height < 1) {
40654 el.setWidth(size.width);
40656 el.setSize(size.width, size.height);
40658 var touch = el.dom.offsetWidth;
40659 this.layout.layout();
40660 // ie requires a double layout on the first pass
40661 if(Roo.isIE && !this.initialized){
40662 this.initialized = true;
40663 this.layout.layout();
40668 // activate all subpanels if not currently active..
40670 setActiveState : function(active){
40671 this.active = active;
40672 this.setActiveClass(active);
40675 this.fireEvent("deactivate", this);
40679 this.fireEvent("activate", this);
40680 // not sure if this should happen before or after..
40681 if (!this.layout) {
40682 return; // should not happen..
40685 for (var r in this.layout.regions) {
40686 reg = this.layout.getRegion(r);
40687 if (reg.getActivePanel()) {
40688 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40689 reg.setActivePanel(reg.getActivePanel());
40692 if (!reg.panels.length) {
40695 reg.showPanel(reg.getPanel(0));
40704 * Returns the nested BorderLayout for this panel
40705 * @return {Roo.BorderLayout}
40707 getLayout : function(){
40708 return this.layout;
40712 * Adds a xtype elements to the layout of the nested panel
40716 xtype : 'ContentPanel',
40723 xtype : 'NestedLayoutPanel',
40729 items : [ ... list of content panels or nested layout panels.. ]
40733 * @param {Object} cfg Xtype definition of item to add.
40735 addxtype : function(cfg) {
40736 return this.layout.addxtype(cfg);
40741 * Ext JS Library 1.1.1
40742 * Copyright(c) 2006-2007, Ext JS, LLC.
40744 * Originally Released Under LGPL - original licence link has changed is not relivant.
40747 * <script type="text/javascript">
40750 * @class Roo.TabPanel
40751 * @extends Roo.util.Observable
40752 * A lightweight tab container.
40756 // basic tabs 1, built from existing content
40757 var tabs = new Roo.TabPanel("tabs1");
40758 tabs.addTab("script", "View Script");
40759 tabs.addTab("markup", "View Markup");
40760 tabs.activate("script");
40762 // more advanced tabs, built from javascript
40763 var jtabs = new Roo.TabPanel("jtabs");
40764 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40766 // set up the UpdateManager
40767 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40768 var updater = tab2.getUpdateManager();
40769 updater.setDefaultUrl("ajax1.htm");
40770 tab2.on('activate', updater.refresh, updater, true);
40772 // Use setUrl for Ajax loading
40773 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40774 tab3.setUrl("ajax2.htm", null, true);
40777 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40780 jtabs.activate("jtabs-1");
40783 * Create a new TabPanel.
40784 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40785 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40787 Roo.bootstrap.panel.Tabs = function(config){
40789 * The container element for this TabPanel.
40790 * @type Roo.Element
40792 this.el = Roo.get(config.el);
40795 if(typeof config == "boolean"){
40796 this.tabPosition = config ? "bottom" : "top";
40798 Roo.apply(this, config);
40802 if(this.tabPosition == "bottom"){
40803 // if tabs are at the bottom = create the body first.
40804 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40805 this.el.addClass("roo-tabs-bottom");
40807 // next create the tabs holders
40809 if (this.tabPosition == "west"){
40811 var reg = this.region; // fake it..
40813 if (!reg.mgr.parent) {
40816 reg = reg.mgr.parent.region;
40818 Roo.log("got nest?");
40820 if (reg.mgr.getRegion('west')) {
40821 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40822 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40823 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40824 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40825 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40833 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40834 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40835 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40836 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40841 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40844 // finally - if tabs are at the top, then create the body last..
40845 if(this.tabPosition != "bottom"){
40846 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40847 * @type Roo.Element
40849 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40850 this.el.addClass("roo-tabs-top");
40854 this.bodyEl.setStyle("position", "relative");
40856 this.active = null;
40857 this.activateDelegate = this.activate.createDelegate(this);
40862 * Fires when the active tab changes
40863 * @param {Roo.TabPanel} this
40864 * @param {Roo.TabPanelItem} activePanel The new active tab
40868 * @event beforetabchange
40869 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40870 * @param {Roo.TabPanel} this
40871 * @param {Object} e Set cancel to true on this object to cancel the tab change
40872 * @param {Roo.TabPanelItem} tab The tab being changed to
40874 "beforetabchange" : true
40877 Roo.EventManager.onWindowResize(this.onResize, this);
40878 this.cpad = this.el.getPadding("lr");
40879 this.hiddenCount = 0;
40882 // toolbar on the tabbar support...
40883 if (this.toolbar) {
40884 alert("no toolbar support yet");
40885 this.toolbar = false;
40887 var tcfg = this.toolbar;
40888 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40889 this.toolbar = new Roo.Toolbar(tcfg);
40890 if (Roo.isSafari) {
40891 var tbl = tcfg.container.child('table', true);
40892 tbl.setAttribute('width', '100%');
40900 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40903 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40905 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40907 tabPosition : "top",
40909 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40911 currentTabWidth : 0,
40913 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40917 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40921 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40923 preferredTabWidth : 175,
40925 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40927 resizeTabs : false,
40929 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40931 monitorResize : true,
40933 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40935 toolbar : false, // set by caller..
40937 region : false, /// set by caller
40939 disableTooltips : true, // not used yet...
40942 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40943 * @param {String} id The id of the div to use <b>or create</b>
40944 * @param {String} text The text for the tab
40945 * @param {String} content (optional) Content to put in the TabPanelItem body
40946 * @param {Boolean} closable (optional) True to create a close icon on the tab
40947 * @return {Roo.TabPanelItem} The created TabPanelItem
40949 addTab : function(id, text, content, closable, tpl)
40951 var item = new Roo.bootstrap.panel.TabItem({
40955 closable : closable,
40958 this.addTabItem(item);
40960 item.setContent(content);
40966 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40967 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40968 * @return {Roo.TabPanelItem}
40970 getTab : function(id){
40971 return this.items[id];
40975 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40976 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40978 hideTab : function(id){
40979 var t = this.items[id];
40982 this.hiddenCount++;
40983 this.autoSizeTabs();
40988 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40989 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40991 unhideTab : function(id){
40992 var t = this.items[id];
40994 t.setHidden(false);
40995 this.hiddenCount--;
40996 this.autoSizeTabs();
41001 * Adds an existing {@link Roo.TabPanelItem}.
41002 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41004 addTabItem : function(item)
41006 this.items[item.id] = item;
41007 this.items.push(item);
41008 this.autoSizeTabs();
41009 // if(this.resizeTabs){
41010 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41011 // this.autoSizeTabs();
41013 // item.autoSize();
41018 * Removes a {@link Roo.TabPanelItem}.
41019 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41021 removeTab : function(id){
41022 var items = this.items;
41023 var tab = items[id];
41024 if(!tab) { return; }
41025 var index = items.indexOf(tab);
41026 if(this.active == tab && items.length > 1){
41027 var newTab = this.getNextAvailable(index);
41032 this.stripEl.dom.removeChild(tab.pnode.dom);
41033 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41034 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41036 items.splice(index, 1);
41037 delete this.items[tab.id];
41038 tab.fireEvent("close", tab);
41039 tab.purgeListeners();
41040 this.autoSizeTabs();
41043 getNextAvailable : function(start){
41044 var items = this.items;
41046 // look for a next tab that will slide over to
41047 // replace the one being removed
41048 while(index < items.length){
41049 var item = items[++index];
41050 if(item && !item.isHidden()){
41054 // if one isn't found select the previous tab (on the left)
41057 var item = items[--index];
41058 if(item && !item.isHidden()){
41066 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41067 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41069 disableTab : function(id){
41070 var tab = this.items[id];
41071 if(tab && this.active != tab){
41077 * Enables a {@link Roo.TabPanelItem} that is disabled.
41078 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41080 enableTab : function(id){
41081 var tab = this.items[id];
41086 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41087 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41088 * @return {Roo.TabPanelItem} The TabPanelItem.
41090 activate : function(id)
41092 //Roo.log('activite:' + id);
41094 var tab = this.items[id];
41098 if(tab == this.active || tab.disabled){
41102 this.fireEvent("beforetabchange", this, e, tab);
41103 if(e.cancel !== true && !tab.disabled){
41105 this.active.hide();
41107 this.active = this.items[id];
41108 this.active.show();
41109 this.fireEvent("tabchange", this, this.active);
41115 * Gets the active {@link Roo.TabPanelItem}.
41116 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41118 getActiveTab : function(){
41119 return this.active;
41123 * Updates the tab body element to fit the height of the container element
41124 * for overflow scrolling
41125 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41127 syncHeight : function(targetHeight){
41128 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41129 var bm = this.bodyEl.getMargins();
41130 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41131 this.bodyEl.setHeight(newHeight);
41135 onResize : function(){
41136 if(this.monitorResize){
41137 this.autoSizeTabs();
41142 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41144 beginUpdate : function(){
41145 this.updating = true;
41149 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41151 endUpdate : function(){
41152 this.updating = false;
41153 this.autoSizeTabs();
41157 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41159 autoSizeTabs : function()
41161 var count = this.items.length;
41162 var vcount = count - this.hiddenCount;
41165 this.stripEl.hide();
41167 this.stripEl.show();
41170 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41175 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41176 var availWidth = Math.floor(w / vcount);
41177 var b = this.stripBody;
41178 if(b.getWidth() > w){
41179 var tabs = this.items;
41180 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41181 if(availWidth < this.minTabWidth){
41182 /*if(!this.sleft){ // incomplete scrolling code
41183 this.createScrollButtons();
41186 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41189 if(this.currentTabWidth < this.preferredTabWidth){
41190 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41196 * Returns the number of tabs in this TabPanel.
41199 getCount : function(){
41200 return this.items.length;
41204 * Resizes all the tabs to the passed width
41205 * @param {Number} The new width
41207 setTabWidth : function(width){
41208 this.currentTabWidth = width;
41209 for(var i = 0, len = this.items.length; i < len; i++) {
41210 if(!this.items[i].isHidden()) {
41211 this.items[i].setWidth(width);
41217 * Destroys this TabPanel
41218 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41220 destroy : function(removeEl){
41221 Roo.EventManager.removeResizeListener(this.onResize, this);
41222 for(var i = 0, len = this.items.length; i < len; i++){
41223 this.items[i].purgeListeners();
41225 if(removeEl === true){
41226 this.el.update("");
41231 createStrip : function(container)
41233 var strip = document.createElement("nav");
41234 strip.className = Roo.bootstrap.version == 4 ?
41235 "navbar-light bg-light" :
41236 "navbar navbar-default"; //"x-tabs-wrap";
41237 container.appendChild(strip);
41241 createStripList : function(strip)
41243 // div wrapper for retard IE
41244 // returns the "tr" element.
41245 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41246 //'<div class="x-tabs-strip-wrap">'+
41247 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41248 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41249 return strip.firstChild; //.firstChild.firstChild.firstChild;
41251 createBody : function(container)
41253 var body = document.createElement("div");
41254 Roo.id(body, "tab-body");
41255 //Roo.fly(body).addClass("x-tabs-body");
41256 Roo.fly(body).addClass("tab-content");
41257 container.appendChild(body);
41260 createItemBody :function(bodyEl, id){
41261 var body = Roo.getDom(id);
41263 body = document.createElement("div");
41266 //Roo.fly(body).addClass("x-tabs-item-body");
41267 Roo.fly(body).addClass("tab-pane");
41268 bodyEl.insertBefore(body, bodyEl.firstChild);
41272 createStripElements : function(stripEl, text, closable, tpl)
41274 var td = document.createElement("li"); // was td..
41275 td.className = 'nav-item';
41277 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41280 stripEl.appendChild(td);
41282 td.className = "x-tabs-closable";
41283 if(!this.closeTpl){
41284 this.closeTpl = new Roo.Template(
41285 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41286 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41287 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41290 var el = this.closeTpl.overwrite(td, {"text": text});
41291 var close = el.getElementsByTagName("div")[0];
41292 var inner = el.getElementsByTagName("em")[0];
41293 return {"el": el, "close": close, "inner": inner};
41296 // not sure what this is..
41297 // if(!this.tabTpl){
41298 //this.tabTpl = new Roo.Template(
41299 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41300 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41302 // this.tabTpl = new Roo.Template(
41303 // '<a href="#">' +
41304 // '<span unselectable="on"' +
41305 // (this.disableTooltips ? '' : ' title="{text}"') +
41306 // ' >{text}</span></a>'
41312 var template = tpl || this.tabTpl || false;
41315 template = new Roo.Template(
41316 Roo.bootstrap.version == 4 ?
41318 '<a class="nav-link" href="#" unselectable="on"' +
41319 (this.disableTooltips ? '' : ' title="{text}"') +
41322 '<a class="nav-link" href="#">' +
41323 '<span unselectable="on"' +
41324 (this.disableTooltips ? '' : ' title="{text}"') +
41325 ' >{text}</span></a>'
41330 switch (typeof(template)) {
41334 template = new Roo.Template(template);
41340 var el = template.overwrite(td, {"text": text});
41342 var inner = el.getElementsByTagName("span")[0];
41344 return {"el": el, "inner": inner};
41352 * @class Roo.TabPanelItem
41353 * @extends Roo.util.Observable
41354 * Represents an individual item (tab plus body) in a TabPanel.
41355 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41356 * @param {String} id The id of this TabPanelItem
41357 * @param {String} text The text for the tab of this TabPanelItem
41358 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41360 Roo.bootstrap.panel.TabItem = function(config){
41362 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41363 * @type Roo.TabPanel
41365 this.tabPanel = config.panel;
41367 * The id for this TabPanelItem
41370 this.id = config.id;
41372 this.disabled = false;
41374 this.text = config.text;
41376 this.loaded = false;
41377 this.closable = config.closable;
41380 * The body element for this TabPanelItem.
41381 * @type Roo.Element
41383 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41384 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41385 this.bodyEl.setStyle("display", "block");
41386 this.bodyEl.setStyle("zoom", "1");
41387 //this.hideAction();
41389 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41391 this.el = Roo.get(els.el);
41392 this.inner = Roo.get(els.inner, true);
41393 this.textEl = Roo.bootstrap.version == 4 ?
41394 this.el : Roo.get(this.el.dom.firstChild, true);
41396 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41397 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41400 // this.el.on("mousedown", this.onTabMouseDown, this);
41401 this.el.on("click", this.onTabClick, this);
41403 if(config.closable){
41404 var c = Roo.get(els.close, true);
41405 c.dom.title = this.closeText;
41406 c.addClassOnOver("close-over");
41407 c.on("click", this.closeClick, this);
41413 * Fires when this tab becomes the active tab.
41414 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41415 * @param {Roo.TabPanelItem} this
41419 * @event beforeclose
41420 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41421 * @param {Roo.TabPanelItem} this
41422 * @param {Object} e Set cancel to true on this object to cancel the close.
41424 "beforeclose": true,
41427 * Fires when this tab is closed.
41428 * @param {Roo.TabPanelItem} this
41432 * @event deactivate
41433 * Fires when this tab is no longer the active tab.
41434 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41435 * @param {Roo.TabPanelItem} this
41437 "deactivate" : true
41439 this.hidden = false;
41441 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41444 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41446 purgeListeners : function(){
41447 Roo.util.Observable.prototype.purgeListeners.call(this);
41448 this.el.removeAllListeners();
41451 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41454 this.status_node.addClass("active");
41457 this.tabPanel.stripWrap.repaint();
41459 this.fireEvent("activate", this.tabPanel, this);
41463 * Returns true if this tab is the active tab.
41464 * @return {Boolean}
41466 isActive : function(){
41467 return this.tabPanel.getActiveTab() == this;
41471 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41474 this.status_node.removeClass("active");
41476 this.fireEvent("deactivate", this.tabPanel, this);
41479 hideAction : function(){
41480 this.bodyEl.hide();
41481 this.bodyEl.setStyle("position", "absolute");
41482 this.bodyEl.setLeft("-20000px");
41483 this.bodyEl.setTop("-20000px");
41486 showAction : function(){
41487 this.bodyEl.setStyle("position", "relative");
41488 this.bodyEl.setTop("");
41489 this.bodyEl.setLeft("");
41490 this.bodyEl.show();
41494 * Set the tooltip for the tab.
41495 * @param {String} tooltip The tab's tooltip
41497 setTooltip : function(text){
41498 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41499 this.textEl.dom.qtip = text;
41500 this.textEl.dom.removeAttribute('title');
41502 this.textEl.dom.title = text;
41506 onTabClick : function(e){
41507 e.preventDefault();
41508 this.tabPanel.activate(this.id);
41511 onTabMouseDown : function(e){
41512 e.preventDefault();
41513 this.tabPanel.activate(this.id);
41516 getWidth : function(){
41517 return this.inner.getWidth();
41520 setWidth : function(width){
41521 var iwidth = width - this.linode.getPadding("lr");
41522 this.inner.setWidth(iwidth);
41523 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41524 this.linode.setWidth(width);
41528 * Show or hide the tab
41529 * @param {Boolean} hidden True to hide or false to show.
41531 setHidden : function(hidden){
41532 this.hidden = hidden;
41533 this.linode.setStyle("display", hidden ? "none" : "");
41537 * Returns true if this tab is "hidden"
41538 * @return {Boolean}
41540 isHidden : function(){
41541 return this.hidden;
41545 * Returns the text for this tab
41548 getText : function(){
41552 autoSize : function(){
41553 //this.el.beginMeasure();
41554 this.textEl.setWidth(1);
41556 * #2804 [new] Tabs in Roojs
41557 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41559 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41560 //this.el.endMeasure();
41564 * Sets the text for the tab (Note: this also sets the tooltip text)
41565 * @param {String} text The tab's text and tooltip
41567 setText : function(text){
41569 this.textEl.update(text);
41570 this.setTooltip(text);
41571 //if(!this.tabPanel.resizeTabs){
41572 // this.autoSize();
41576 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41578 activate : function(){
41579 this.tabPanel.activate(this.id);
41583 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41585 disable : function(){
41586 if(this.tabPanel.active != this){
41587 this.disabled = true;
41588 this.status_node.addClass("disabled");
41593 * Enables this TabPanelItem if it was previously disabled.
41595 enable : function(){
41596 this.disabled = false;
41597 this.status_node.removeClass("disabled");
41601 * Sets the content for this TabPanelItem.
41602 * @param {String} content The content
41603 * @param {Boolean} loadScripts true to look for and load scripts
41605 setContent : function(content, loadScripts){
41606 this.bodyEl.update(content, loadScripts);
41610 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41611 * @return {Roo.UpdateManager} The UpdateManager
41613 getUpdateManager : function(){
41614 return this.bodyEl.getUpdateManager();
41618 * Set a URL to be used to load the content for this TabPanelItem.
41619 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41620 * @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)
41621 * @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)
41622 * @return {Roo.UpdateManager} The UpdateManager
41624 setUrl : function(url, params, loadOnce){
41625 if(this.refreshDelegate){
41626 this.un('activate', this.refreshDelegate);
41628 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41629 this.on("activate", this.refreshDelegate);
41630 return this.bodyEl.getUpdateManager();
41634 _handleRefresh : function(url, params, loadOnce){
41635 if(!loadOnce || !this.loaded){
41636 var updater = this.bodyEl.getUpdateManager();
41637 updater.update(url, params, this._setLoaded.createDelegate(this));
41642 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41643 * Will fail silently if the setUrl method has not been called.
41644 * This does not activate the panel, just updates its content.
41646 refresh : function(){
41647 if(this.refreshDelegate){
41648 this.loaded = false;
41649 this.refreshDelegate();
41654 _setLoaded : function(){
41655 this.loaded = true;
41659 closeClick : function(e){
41662 this.fireEvent("beforeclose", this, o);
41663 if(o.cancel !== true){
41664 this.tabPanel.removeTab(this.id);
41668 * The text displayed in the tooltip for the close icon.
41671 closeText : "Close this tab"
41674 * This script refer to:
41675 * Title: International Telephone Input
41676 * Author: Jack O'Connor
41677 * Code version: v12.1.12
41678 * Availability: https://github.com/jackocnr/intl-tel-input.git
41681 Roo.bootstrap.PhoneInputData = function() {
41684 "Afghanistan (افغانستان)",
41689 "Albania (Shqipëri)",
41694 "Algeria (الجزائر)",
41719 "Antigua and Barbuda",
41729 "Armenia (Հայաստան)",
41745 "Austria (Österreich)",
41750 "Azerbaijan (Azərbaycan)",
41760 "Bahrain (البحرين)",
41765 "Bangladesh (বাংলাদেশ)",
41775 "Belarus (Беларусь)",
41780 "Belgium (België)",
41810 "Bosnia and Herzegovina (Босна и Херцеговина)",
41825 "British Indian Ocean Territory",
41830 "British Virgin Islands",
41840 "Bulgaria (България)",
41850 "Burundi (Uburundi)",
41855 "Cambodia (កម្ពុជា)",
41860 "Cameroon (Cameroun)",
41869 ["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"]
41872 "Cape Verde (Kabu Verdi)",
41877 "Caribbean Netherlands",
41888 "Central African Republic (République centrafricaine)",
41908 "Christmas Island",
41914 "Cocos (Keeling) Islands",
41925 "Comoros (جزر القمر)",
41930 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41935 "Congo (Republic) (Congo-Brazzaville)",
41955 "Croatia (Hrvatska)",
41976 "Czech Republic (Česká republika)",
41981 "Denmark (Danmark)",
41996 "Dominican Republic (República Dominicana)",
42000 ["809", "829", "849"]
42018 "Equatorial Guinea (Guinea Ecuatorial)",
42038 "Falkland Islands (Islas Malvinas)",
42043 "Faroe Islands (Føroyar)",
42064 "French Guiana (Guyane française)",
42069 "French Polynesia (Polynésie française)",
42084 "Georgia (საქართველო)",
42089 "Germany (Deutschland)",
42109 "Greenland (Kalaallit Nunaat)",
42146 "Guinea-Bissau (Guiné Bissau)",
42171 "Hungary (Magyarország)",
42176 "Iceland (Ísland)",
42196 "Iraq (العراق)",
42212 "Israel (ישראל)",
42239 "Jordan (الأردن)",
42244 "Kazakhstan (Казахстан)",
42265 "Kuwait (الكويت)",
42270 "Kyrgyzstan (Кыргызстан)",
42280 "Latvia (Latvija)",
42285 "Lebanon (لبنان)",
42300 "Libya (ليبيا)",
42310 "Lithuania (Lietuva)",
42325 "Macedonia (FYROM) (Македонија)",
42330 "Madagascar (Madagasikara)",
42360 "Marshall Islands",
42370 "Mauritania (موريتانيا)",
42375 "Mauritius (Moris)",
42396 "Moldova (Republica Moldova)",
42406 "Mongolia (Монгол)",
42411 "Montenegro (Crna Gora)",
42421 "Morocco (المغرب)",
42427 "Mozambique (Moçambique)",
42432 "Myanmar (Burma) (မြန်မာ)",
42437 "Namibia (Namibië)",
42452 "Netherlands (Nederland)",
42457 "New Caledonia (Nouvelle-Calédonie)",
42492 "North Korea (조선 민주주의 인민 공화국)",
42497 "Northern Mariana Islands",
42513 "Pakistan (پاکستان)",
42523 "Palestine (فلسطين)",
42533 "Papua New Guinea",
42575 "Réunion (La Réunion)",
42581 "Romania (România)",
42597 "Saint Barthélemy",
42608 "Saint Kitts and Nevis",
42618 "Saint Martin (Saint-Martin (partie française))",
42624 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42629 "Saint Vincent and the Grenadines",
42644 "São Tomé and Príncipe (São Tomé e Príncipe)",
42649 "Saudi Arabia (المملكة العربية السعودية)",
42654 "Senegal (Sénégal)",
42684 "Slovakia (Slovensko)",
42689 "Slovenia (Slovenija)",
42699 "Somalia (Soomaaliya)",
42709 "South Korea (대한민국)",
42714 "South Sudan (جنوب السودان)",
42724 "Sri Lanka (ශ්රී ලංකාව)",
42729 "Sudan (السودان)",
42739 "Svalbard and Jan Mayen",
42750 "Sweden (Sverige)",
42755 "Switzerland (Schweiz)",
42760 "Syria (سوريا)",
42805 "Trinidad and Tobago",
42810 "Tunisia (تونس)",
42815 "Turkey (Türkiye)",
42825 "Turks and Caicos Islands",
42835 "U.S. Virgin Islands",
42845 "Ukraine (Україна)",
42850 "United Arab Emirates (الإمارات العربية المتحدة)",
42872 "Uzbekistan (Oʻzbekiston)",
42882 "Vatican City (Città del Vaticano)",
42893 "Vietnam (Việt Nam)",
42898 "Wallis and Futuna (Wallis-et-Futuna)",
42903 "Western Sahara (الصحراء الغربية)",
42909 "Yemen (اليمن)",
42933 * This script refer to:
42934 * Title: International Telephone Input
42935 * Author: Jack O'Connor
42936 * Code version: v12.1.12
42937 * Availability: https://github.com/jackocnr/intl-tel-input.git
42941 * @class Roo.bootstrap.PhoneInput
42942 * @extends Roo.bootstrap.TriggerField
42943 * An input with International dial-code selection
42945 * @cfg {String} defaultDialCode default '+852'
42946 * @cfg {Array} preferedCountries default []
42949 * Create a new PhoneInput.
42950 * @param {Object} config Configuration options
42953 Roo.bootstrap.PhoneInput = function(config) {
42954 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42957 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42959 listWidth: undefined,
42961 selectedClass: 'active',
42963 invalidClass : "has-warning",
42965 validClass: 'has-success',
42967 allowed: '0123456789',
42972 * @cfg {String} defaultDialCode The default dial code when initializing the input
42974 defaultDialCode: '+852',
42977 * @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
42979 preferedCountries: false,
42981 getAutoCreate : function()
42983 var data = Roo.bootstrap.PhoneInputData();
42984 var align = this.labelAlign || this.parentLabelAlign();
42987 this.allCountries = [];
42988 this.dialCodeMapping = [];
42990 for (var i = 0; i < data.length; i++) {
42992 this.allCountries[i] = {
42996 priority: c[3] || 0,
42997 areaCodes: c[4] || null
42999 this.dialCodeMapping[c[2]] = {
43002 priority: c[3] || 0,
43003 areaCodes: c[4] || null
43015 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43016 maxlength: this.max_length,
43017 cls : 'form-control tel-input',
43018 autocomplete: 'new-password'
43021 var hiddenInput = {
43024 cls: 'hidden-tel-input'
43028 hiddenInput.name = this.name;
43031 if (this.disabled) {
43032 input.disabled = true;
43035 var flag_container = {
43052 cls: this.hasFeedback ? 'has-feedback' : '',
43058 cls: 'dial-code-holder',
43065 cls: 'roo-select2-container input-group',
43072 if (this.fieldLabel.length) {
43075 tooltip: 'This field is required'
43081 cls: 'control-label',
43087 html: this.fieldLabel
43090 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43096 if(this.indicatorpos == 'right') {
43097 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43104 if(align == 'left') {
43112 if(this.labelWidth > 12){
43113 label.style = "width: " + this.labelWidth + 'px';
43115 if(this.labelWidth < 13 && this.labelmd == 0){
43116 this.labelmd = this.labelWidth;
43118 if(this.labellg > 0){
43119 label.cls += ' col-lg-' + this.labellg;
43120 input.cls += ' col-lg-' + (12 - this.labellg);
43122 if(this.labelmd > 0){
43123 label.cls += ' col-md-' + this.labelmd;
43124 container.cls += ' col-md-' + (12 - this.labelmd);
43126 if(this.labelsm > 0){
43127 label.cls += ' col-sm-' + this.labelsm;
43128 container.cls += ' col-sm-' + (12 - this.labelsm);
43130 if(this.labelxs > 0){
43131 label.cls += ' col-xs-' + this.labelxs;
43132 container.cls += ' col-xs-' + (12 - this.labelxs);
43142 var settings = this;
43144 ['xs','sm','md','lg'].map(function(size){
43145 if (settings[size]) {
43146 cfg.cls += ' col-' + size + '-' + settings[size];
43150 this.store = new Roo.data.Store({
43151 proxy : new Roo.data.MemoryProxy({}),
43152 reader : new Roo.data.JsonReader({
43163 'name' : 'dialCode',
43167 'name' : 'priority',
43171 'name' : 'areaCodes',
43178 if(!this.preferedCountries) {
43179 this.preferedCountries = [
43186 var p = this.preferedCountries.reverse();
43189 for (var i = 0; i < p.length; i++) {
43190 for (var j = 0; j < this.allCountries.length; j++) {
43191 if(this.allCountries[j].iso2 == p[i]) {
43192 var t = this.allCountries[j];
43193 this.allCountries.splice(j,1);
43194 this.allCountries.unshift(t);
43200 this.store.proxy.data = {
43202 data: this.allCountries
43208 initEvents : function()
43211 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43213 this.indicator = this.indicatorEl();
43214 this.flag = this.flagEl();
43215 this.dialCodeHolder = this.dialCodeHolderEl();
43217 this.trigger = this.el.select('div.flag-box',true).first();
43218 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43223 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43224 _this.list.setWidth(lw);
43227 this.list.on('mouseover', this.onViewOver, this);
43228 this.list.on('mousemove', this.onViewMove, this);
43229 this.inputEl().on("keyup", this.onKeyUp, this);
43230 this.inputEl().on("keypress", this.onKeyPress, this);
43232 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43234 this.view = new Roo.View(this.list, this.tpl, {
43235 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43238 this.view.on('click', this.onViewClick, this);
43239 this.setValue(this.defaultDialCode);
43242 onTriggerClick : function(e)
43244 Roo.log('trigger click');
43249 if(this.isExpanded()){
43251 this.hasFocus = false;
43253 this.store.load({});
43254 this.hasFocus = true;
43259 isExpanded : function()
43261 return this.list.isVisible();
43264 collapse : function()
43266 if(!this.isExpanded()){
43270 Roo.get(document).un('mousedown', this.collapseIf, this);
43271 Roo.get(document).un('mousewheel', this.collapseIf, this);
43272 this.fireEvent('collapse', this);
43276 expand : function()
43280 if(this.isExpanded() || !this.hasFocus){
43284 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43285 this.list.setWidth(lw);
43288 this.restrictHeight();
43290 Roo.get(document).on('mousedown', this.collapseIf, this);
43291 Roo.get(document).on('mousewheel', this.collapseIf, this);
43293 this.fireEvent('expand', this);
43296 restrictHeight : function()
43298 this.list.alignTo(this.inputEl(), this.listAlign);
43299 this.list.alignTo(this.inputEl(), this.listAlign);
43302 onViewOver : function(e, t)
43304 if(this.inKeyMode){
43307 var item = this.view.findItemFromChild(t);
43310 var index = this.view.indexOf(item);
43311 this.select(index, false);
43316 onViewClick : function(view, doFocus, el, e)
43318 var index = this.view.getSelectedIndexes()[0];
43320 var r = this.store.getAt(index);
43323 this.onSelect(r, index);
43325 if(doFocus !== false && !this.blockFocus){
43326 this.inputEl().focus();
43330 onViewMove : function(e, t)
43332 this.inKeyMode = false;
43335 select : function(index, scrollIntoView)
43337 this.selectedIndex = index;
43338 this.view.select(index);
43339 if(scrollIntoView !== false){
43340 var el = this.view.getNode(index);
43342 this.list.scrollChildIntoView(el, false);
43347 createList : function()
43349 this.list = Roo.get(document.body).createChild({
43351 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43352 style: 'display:none'
43355 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43358 collapseIf : function(e)
43360 var in_combo = e.within(this.el);
43361 var in_list = e.within(this.list);
43362 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43364 if (in_combo || in_list || is_list) {
43370 onSelect : function(record, index)
43372 if(this.fireEvent('beforeselect', this, record, index) !== false){
43374 this.setFlagClass(record.data.iso2);
43375 this.setDialCode(record.data.dialCode);
43376 this.hasFocus = false;
43378 this.fireEvent('select', this, record, index);
43382 flagEl : function()
43384 var flag = this.el.select('div.flag',true).first();
43391 dialCodeHolderEl : function()
43393 var d = this.el.select('input.dial-code-holder',true).first();
43400 setDialCode : function(v)
43402 this.dialCodeHolder.dom.value = '+'+v;
43405 setFlagClass : function(n)
43407 this.flag.dom.className = 'flag '+n;
43410 getValue : function()
43412 var v = this.inputEl().getValue();
43413 if(this.dialCodeHolder) {
43414 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43419 setValue : function(v)
43421 var d = this.getDialCode(v);
43423 //invalid dial code
43424 if(v.length == 0 || !d || d.length == 0) {
43426 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43427 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43433 this.setFlagClass(this.dialCodeMapping[d].iso2);
43434 this.setDialCode(d);
43435 this.inputEl().dom.value = v.replace('+'+d,'');
43436 this.hiddenEl().dom.value = this.getValue();
43441 getDialCode : function(v)
43445 if (v.length == 0) {
43446 return this.dialCodeHolder.dom.value;
43450 if (v.charAt(0) != "+") {
43453 var numericChars = "";
43454 for (var i = 1; i < v.length; i++) {
43455 var c = v.charAt(i);
43458 if (this.dialCodeMapping[numericChars]) {
43459 dialCode = v.substr(1, i);
43461 if (numericChars.length == 4) {
43471 this.setValue(this.defaultDialCode);
43475 hiddenEl : function()
43477 return this.el.select('input.hidden-tel-input',true).first();
43480 // after setting val
43481 onKeyUp : function(e){
43482 this.setValue(this.getValue());
43485 onKeyPress : function(e){
43486 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43493 * @class Roo.bootstrap.MoneyField
43494 * @extends Roo.bootstrap.ComboBox
43495 * Bootstrap MoneyField class
43498 * Create a new MoneyField.
43499 * @param {Object} config Configuration options
43502 Roo.bootstrap.MoneyField = function(config) {
43504 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43508 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43511 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43513 allowDecimals : true,
43515 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43517 decimalSeparator : ".",
43519 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43521 decimalPrecision : 0,
43523 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43525 allowNegative : true,
43527 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43531 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43533 minValue : Number.NEGATIVE_INFINITY,
43535 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43537 maxValue : Number.MAX_VALUE,
43539 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43541 minText : "The minimum value for this field is {0}",
43543 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43545 maxText : "The maximum value for this field is {0}",
43547 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43548 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43550 nanText : "{0} is not a valid number",
43552 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43556 * @cfg {String} defaults currency of the MoneyField
43557 * value should be in lkey
43559 defaultCurrency : false,
43561 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43563 thousandsDelimiter : false,
43565 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43576 getAutoCreate : function()
43578 var align = this.labelAlign || this.parentLabelAlign();
43590 cls : 'form-control roo-money-amount-input',
43591 autocomplete: 'new-password'
43594 var hiddenInput = {
43598 cls: 'hidden-number-input'
43601 if(this.max_length) {
43602 input.maxlength = this.max_length;
43606 hiddenInput.name = this.name;
43609 if (this.disabled) {
43610 input.disabled = true;
43613 var clg = 12 - this.inputlg;
43614 var cmd = 12 - this.inputmd;
43615 var csm = 12 - this.inputsm;
43616 var cxs = 12 - this.inputxs;
43620 cls : 'row roo-money-field',
43624 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43628 cls: 'roo-select2-container input-group',
43632 cls : 'form-control roo-money-currency-input',
43633 autocomplete: 'new-password',
43635 name : this.currencyName
43639 cls : 'input-group-addon',
43653 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43657 cls: this.hasFeedback ? 'has-feedback' : '',
43668 if (this.fieldLabel.length) {
43671 tooltip: 'This field is required'
43677 cls: 'control-label',
43683 html: this.fieldLabel
43686 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43692 if(this.indicatorpos == 'right') {
43693 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43700 if(align == 'left') {
43708 if(this.labelWidth > 12){
43709 label.style = "width: " + this.labelWidth + 'px';
43711 if(this.labelWidth < 13 && this.labelmd == 0){
43712 this.labelmd = this.labelWidth;
43714 if(this.labellg > 0){
43715 label.cls += ' col-lg-' + this.labellg;
43716 input.cls += ' col-lg-' + (12 - this.labellg);
43718 if(this.labelmd > 0){
43719 label.cls += ' col-md-' + this.labelmd;
43720 container.cls += ' col-md-' + (12 - this.labelmd);
43722 if(this.labelsm > 0){
43723 label.cls += ' col-sm-' + this.labelsm;
43724 container.cls += ' col-sm-' + (12 - this.labelsm);
43726 if(this.labelxs > 0){
43727 label.cls += ' col-xs-' + this.labelxs;
43728 container.cls += ' col-xs-' + (12 - this.labelxs);
43739 var settings = this;
43741 ['xs','sm','md','lg'].map(function(size){
43742 if (settings[size]) {
43743 cfg.cls += ' col-' + size + '-' + settings[size];
43750 initEvents : function()
43752 this.indicator = this.indicatorEl();
43754 this.initCurrencyEvent();
43756 this.initNumberEvent();
43759 initCurrencyEvent : function()
43762 throw "can not find store for combo";
43765 this.store = Roo.factory(this.store, Roo.data);
43766 this.store.parent = this;
43770 this.triggerEl = this.el.select('.input-group-addon', true).first();
43772 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43777 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43778 _this.list.setWidth(lw);
43781 this.list.on('mouseover', this.onViewOver, this);
43782 this.list.on('mousemove', this.onViewMove, this);
43783 this.list.on('scroll', this.onViewScroll, this);
43786 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43789 this.view = new Roo.View(this.list, this.tpl, {
43790 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43793 this.view.on('click', this.onViewClick, this);
43795 this.store.on('beforeload', this.onBeforeLoad, this);
43796 this.store.on('load', this.onLoad, this);
43797 this.store.on('loadexception', this.onLoadException, this);
43799 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43800 "up" : function(e){
43801 this.inKeyMode = true;
43805 "down" : function(e){
43806 if(!this.isExpanded()){
43807 this.onTriggerClick();
43809 this.inKeyMode = true;
43814 "enter" : function(e){
43817 if(this.fireEvent("specialkey", this, e)){
43818 this.onViewClick(false);
43824 "esc" : function(e){
43828 "tab" : function(e){
43831 if(this.fireEvent("specialkey", this, e)){
43832 this.onViewClick(false);
43840 doRelay : function(foo, bar, hname){
43841 if(hname == 'down' || this.scope.isExpanded()){
43842 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43850 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43854 initNumberEvent : function(e)
43856 this.inputEl().on("keydown" , this.fireKey, this);
43857 this.inputEl().on("focus", this.onFocus, this);
43858 this.inputEl().on("blur", this.onBlur, this);
43860 this.inputEl().relayEvent('keyup', this);
43862 if(this.indicator){
43863 this.indicator.addClass('invisible');
43866 this.originalValue = this.getValue();
43868 if(this.validationEvent == 'keyup'){
43869 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43870 this.inputEl().on('keyup', this.filterValidation, this);
43872 else if(this.validationEvent !== false){
43873 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43876 if(this.selectOnFocus){
43877 this.on("focus", this.preFocus, this);
43880 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43881 this.inputEl().on("keypress", this.filterKeys, this);
43883 this.inputEl().relayEvent('keypress', this);
43886 var allowed = "0123456789";
43888 if(this.allowDecimals){
43889 allowed += this.decimalSeparator;
43892 if(this.allowNegative){
43896 if(this.thousandsDelimiter) {
43900 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43902 var keyPress = function(e){
43904 var k = e.getKey();
43906 var c = e.getCharCode();
43909 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43910 allowed.indexOf(String.fromCharCode(c)) === -1
43916 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43920 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43925 this.inputEl().on("keypress", keyPress, this);
43929 onTriggerClick : function(e)
43936 this.loadNext = false;
43938 if(this.isExpanded()){
43943 this.hasFocus = true;
43945 if(this.triggerAction == 'all') {
43946 this.doQuery(this.allQuery, true);
43950 this.doQuery(this.getRawValue());
43953 getCurrency : function()
43955 var v = this.currencyEl().getValue();
43960 restrictHeight : function()
43962 this.list.alignTo(this.currencyEl(), this.listAlign);
43963 this.list.alignTo(this.currencyEl(), this.listAlign);
43966 onViewClick : function(view, doFocus, el, e)
43968 var index = this.view.getSelectedIndexes()[0];
43970 var r = this.store.getAt(index);
43973 this.onSelect(r, index);
43977 onSelect : function(record, index){
43979 if(this.fireEvent('beforeselect', this, record, index) !== false){
43981 this.setFromCurrencyData(index > -1 ? record.data : false);
43985 this.fireEvent('select', this, record, index);
43989 setFromCurrencyData : function(o)
43993 this.lastCurrency = o;
43995 if (this.currencyField) {
43996 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43998 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44001 this.lastSelectionText = currency;
44003 //setting default currency
44004 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44005 this.setCurrency(this.defaultCurrency);
44009 this.setCurrency(currency);
44012 setFromData : function(o)
44016 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44018 this.setFromCurrencyData(c);
44023 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44025 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44028 this.setValue(value);
44032 setCurrency : function(v)
44034 this.currencyValue = v;
44037 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44042 setValue : function(v)
44044 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44050 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44052 this.inputEl().dom.value = (v == '') ? '' :
44053 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44055 if(!this.allowZero && v === '0') {
44056 this.hiddenEl().dom.value = '';
44057 this.inputEl().dom.value = '';
44064 getRawValue : function()
44066 var v = this.inputEl().getValue();
44071 getValue : function()
44073 return this.fixPrecision(this.parseValue(this.getRawValue()));
44076 parseValue : function(value)
44078 if(this.thousandsDelimiter) {
44080 r = new RegExp(",", "g");
44081 value = value.replace(r, "");
44084 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44085 return isNaN(value) ? '' : value;
44089 fixPrecision : function(value)
44091 if(this.thousandsDelimiter) {
44093 r = new RegExp(",", "g");
44094 value = value.replace(r, "");
44097 var nan = isNaN(value);
44099 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44100 return nan ? '' : value;
44102 return parseFloat(value).toFixed(this.decimalPrecision);
44105 decimalPrecisionFcn : function(v)
44107 return Math.floor(v);
44110 validateValue : function(value)
44112 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44116 var num = this.parseValue(value);
44119 this.markInvalid(String.format(this.nanText, value));
44123 if(num < this.minValue){
44124 this.markInvalid(String.format(this.minText, this.minValue));
44128 if(num > this.maxValue){
44129 this.markInvalid(String.format(this.maxText, this.maxValue));
44136 validate : function()
44138 if(this.disabled || this.allowBlank){
44143 var currency = this.getCurrency();
44145 if(this.validateValue(this.getRawValue()) && currency.length){
44150 this.markInvalid();
44154 getName: function()
44159 beforeBlur : function()
44165 var v = this.parseValue(this.getRawValue());
44172 onBlur : function()
44176 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44177 //this.el.removeClass(this.focusClass);
44180 this.hasFocus = false;
44182 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44186 var v = this.getValue();
44188 if(String(v) !== String(this.startValue)){
44189 this.fireEvent('change', this, v, this.startValue);
44192 this.fireEvent("blur", this);
44195 inputEl : function()
44197 return this.el.select('.roo-money-amount-input', true).first();
44200 currencyEl : function()
44202 return this.el.select('.roo-money-currency-input', true).first();
44205 hiddenEl : function()
44207 return this.el.select('input.hidden-number-input',true).first();
44211 * @class Roo.bootstrap.BezierSignature
44212 * @extends Roo.bootstrap.Component
44213 * Bootstrap BezierSignature class
44214 * This script refer to:
44215 * Title: Signature Pad
44217 * Availability: https://github.com/szimek/signature_pad
44220 * Create a new BezierSignature
44221 * @param {Object} config The config object
44224 Roo.bootstrap.BezierSignature = function(config){
44225 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44231 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44238 mouse_btn_down: true,
44241 * @cfg {int} canvas height
44243 canvas_height: '200px',
44246 * @cfg {float|function} Radius of a single dot.
44251 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44256 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44261 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44266 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44271 * @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.
44273 bg_color: 'rgba(0, 0, 0, 0)',
44276 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44278 dot_color: 'black',
44281 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44283 velocity_filter_weight: 0.7,
44286 * @cfg {function} Callback when stroke begin.
44291 * @cfg {function} Callback when stroke end.
44295 getAutoCreate : function()
44297 var cls = 'roo-signature column';
44300 cls += ' ' + this.cls;
44310 for(var i = 0; i < col_sizes.length; i++) {
44311 if(this[col_sizes[i]]) {
44312 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44322 cls: 'roo-signature-body',
44326 cls: 'roo-signature-body-canvas',
44327 height: this.canvas_height,
44328 width: this.canvas_width
44335 style: 'display: none'
44343 initEvents: function()
44345 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44347 var canvas = this.canvasEl();
44349 // mouse && touch event swapping...
44350 canvas.dom.style.touchAction = 'none';
44351 canvas.dom.style.msTouchAction = 'none';
44353 this.mouse_btn_down = false;
44354 canvas.on('mousedown', this._handleMouseDown, this);
44355 canvas.on('mousemove', this._handleMouseMove, this);
44356 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44358 if (window.PointerEvent) {
44359 canvas.on('pointerdown', this._handleMouseDown, this);
44360 canvas.on('pointermove', this._handleMouseMove, this);
44361 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44364 if ('ontouchstart' in window) {
44365 canvas.on('touchstart', this._handleTouchStart, this);
44366 canvas.on('touchmove', this._handleTouchMove, this);
44367 canvas.on('touchend', this._handleTouchEnd, this);
44370 Roo.EventManager.onWindowResize(this.resize, this, true);
44372 // file input event
44373 this.fileEl().on('change', this.uploadImage, this);
44380 resize: function(){
44382 var canvas = this.canvasEl().dom;
44383 var ctx = this.canvasElCtx();
44384 var img_data = false;
44386 if(canvas.width > 0) {
44387 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44389 // setting canvas width will clean img data
44392 var style = window.getComputedStyle ?
44393 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44395 var padding_left = parseInt(style.paddingLeft) || 0;
44396 var padding_right = parseInt(style.paddingRight) || 0;
44398 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44401 ctx.putImageData(img_data, 0, 0);
44405 _handleMouseDown: function(e)
44407 if (e.browserEvent.which === 1) {
44408 this.mouse_btn_down = true;
44409 this.strokeBegin(e);
44413 _handleMouseMove: function (e)
44415 if (this.mouse_btn_down) {
44416 this.strokeMoveUpdate(e);
44420 _handleMouseUp: function (e)
44422 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44423 this.mouse_btn_down = false;
44428 _handleTouchStart: function (e) {
44430 e.preventDefault();
44431 if (e.browserEvent.targetTouches.length === 1) {
44432 // var touch = e.browserEvent.changedTouches[0];
44433 // this.strokeBegin(touch);
44435 this.strokeBegin(e); // assume e catching the correct xy...
44439 _handleTouchMove: function (e) {
44440 e.preventDefault();
44441 // var touch = event.targetTouches[0];
44442 // _this._strokeMoveUpdate(touch);
44443 this.strokeMoveUpdate(e);
44446 _handleTouchEnd: function (e) {
44447 var wasCanvasTouched = e.target === this.canvasEl().dom;
44448 if (wasCanvasTouched) {
44449 e.preventDefault();
44450 // var touch = event.changedTouches[0];
44451 // _this._strokeEnd(touch);
44456 reset: function () {
44457 this._lastPoints = [];
44458 this._lastVelocity = 0;
44459 this._lastWidth = (this.min_width + this.max_width) / 2;
44460 this.canvasElCtx().fillStyle = this.dot_color;
44463 strokeMoveUpdate: function(e)
44465 this.strokeUpdate(e);
44467 if (this.throttle) {
44468 this.throttleStroke(this.strokeUpdate, this.throttle);
44471 this.strokeUpdate(e);
44475 strokeBegin: function(e)
44477 var newPointGroup = {
44478 color: this.dot_color,
44482 if (typeof this.onBegin === 'function') {
44486 this.curve_data.push(newPointGroup);
44488 this.strokeUpdate(e);
44491 strokeUpdate: function(e)
44493 var rect = this.canvasEl().dom.getBoundingClientRect();
44494 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44495 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44496 var lastPoints = lastPointGroup.points;
44497 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44498 var isLastPointTooClose = lastPoint
44499 ? point.distanceTo(lastPoint) <= this.min_distance
44501 var color = lastPointGroup.color;
44502 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44503 var curve = this.addPoint(point);
44505 this.drawDot({color: color, point: point});
44508 this.drawCurve({color: color, curve: curve});
44518 strokeEnd: function(e)
44520 this.strokeUpdate(e);
44521 if (typeof this.onEnd === 'function') {
44526 addPoint: function (point) {
44527 var _lastPoints = this._lastPoints;
44528 _lastPoints.push(point);
44529 if (_lastPoints.length > 2) {
44530 if (_lastPoints.length === 3) {
44531 _lastPoints.unshift(_lastPoints[0]);
44533 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44534 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44535 _lastPoints.shift();
44541 calculateCurveWidths: function (startPoint, endPoint) {
44542 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44543 (1 - this.velocity_filter_weight) * this._lastVelocity;
44545 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44548 start: this._lastWidth
44551 this._lastVelocity = velocity;
44552 this._lastWidth = newWidth;
44556 drawDot: function (_a) {
44557 var color = _a.color, point = _a.point;
44558 var ctx = this.canvasElCtx();
44559 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44561 this.drawCurveSegment(point.x, point.y, width);
44563 ctx.fillStyle = color;
44567 drawCurve: function (_a) {
44568 var color = _a.color, curve = _a.curve;
44569 var ctx = this.canvasElCtx();
44570 var widthDelta = curve.endWidth - curve.startWidth;
44571 var drawSteps = Math.floor(curve.length()) * 2;
44573 ctx.fillStyle = color;
44574 for (var i = 0; i < drawSteps; i += 1) {
44575 var t = i / drawSteps;
44581 var x = uuu * curve.startPoint.x;
44582 x += 3 * uu * t * curve.control1.x;
44583 x += 3 * u * tt * curve.control2.x;
44584 x += ttt * curve.endPoint.x;
44585 var y = uuu * curve.startPoint.y;
44586 y += 3 * uu * t * curve.control1.y;
44587 y += 3 * u * tt * curve.control2.y;
44588 y += ttt * curve.endPoint.y;
44589 var width = curve.startWidth + ttt * widthDelta;
44590 this.drawCurveSegment(x, y, width);
44596 drawCurveSegment: function (x, y, width) {
44597 var ctx = this.canvasElCtx();
44599 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44600 this.is_empty = false;
44605 var ctx = this.canvasElCtx();
44606 var canvas = this.canvasEl().dom;
44607 ctx.fillStyle = this.bg_color;
44608 ctx.clearRect(0, 0, canvas.width, canvas.height);
44609 ctx.fillRect(0, 0, canvas.width, canvas.height);
44610 this.curve_data = [];
44612 this.is_empty = true;
44617 return this.el.select('input',true).first();
44620 canvasEl: function()
44622 return this.el.select('canvas',true).first();
44625 canvasElCtx: function()
44627 return this.el.select('canvas',true).first().dom.getContext('2d');
44630 getImage: function(type)
44632 if(this.is_empty) {
44637 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44640 drawFromImage: function(img_src)
44642 var img = new Image();
44644 img.onload = function(){
44645 this.canvasElCtx().drawImage(img, 0, 0);
44650 this.is_empty = false;
44653 selectImage: function()
44655 this.fileEl().dom.click();
44658 uploadImage: function(e)
44660 var reader = new FileReader();
44662 reader.onload = function(e){
44663 var img = new Image();
44664 img.onload = function(){
44666 this.canvasElCtx().drawImage(img, 0, 0);
44668 img.src = e.target.result;
44671 reader.readAsDataURL(e.target.files[0]);
44674 // Bezier Point Constructor
44675 Point: (function () {
44676 function Point(x, y, time) {
44679 this.time = time || Date.now();
44681 Point.prototype.distanceTo = function (start) {
44682 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44684 Point.prototype.equals = function (other) {
44685 return this.x === other.x && this.y === other.y && this.time === other.time;
44687 Point.prototype.velocityFrom = function (start) {
44688 return this.time !== start.time
44689 ? this.distanceTo(start) / (this.time - start.time)
44696 // Bezier Constructor
44697 Bezier: (function () {
44698 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44699 this.startPoint = startPoint;
44700 this.control2 = control2;
44701 this.control1 = control1;
44702 this.endPoint = endPoint;
44703 this.startWidth = startWidth;
44704 this.endWidth = endWidth;
44706 Bezier.fromPoints = function (points, widths, scope) {
44707 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44708 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44709 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44711 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44712 var dx1 = s1.x - s2.x;
44713 var dy1 = s1.y - s2.y;
44714 var dx2 = s2.x - s3.x;
44715 var dy2 = s2.y - s3.y;
44716 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44717 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44718 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44719 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44720 var dxm = m1.x - m2.x;
44721 var dym = m1.y - m2.y;
44722 var k = l2 / (l1 + l2);
44723 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44724 var tx = s2.x - cm.x;
44725 var ty = s2.y - cm.y;
44727 c1: new scope.Point(m1.x + tx, m1.y + ty),
44728 c2: new scope.Point(m2.x + tx, m2.y + ty)
44731 Bezier.prototype.length = function () {
44736 for (var i = 0; i <= steps; i += 1) {
44738 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44739 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44741 var xdiff = cx - px;
44742 var ydiff = cy - py;
44743 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44750 Bezier.prototype.point = function (t, start, c1, c2, end) {
44751 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44752 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44753 + (3.0 * c2 * (1.0 - t) * t * t)
44754 + (end * t * t * t);
44759 throttleStroke: function(fn, wait) {
44760 if (wait === void 0) { wait = 250; }
44762 var timeout = null;
44766 var later = function () {
44767 previous = Date.now();
44769 result = fn.apply(storedContext, storedArgs);
44771 storedContext = null;
44775 return function wrapper() {
44777 for (var _i = 0; _i < arguments.length; _i++) {
44778 args[_i] = arguments[_i];
44780 var now = Date.now();
44781 var remaining = wait - (now - previous);
44782 storedContext = this;
44784 if (remaining <= 0 || remaining > wait) {
44786 clearTimeout(timeout);
44790 result = fn.apply(storedContext, storedArgs);
44792 storedContext = null;
44796 else if (!timeout) {
44797 timeout = window.setTimeout(later, remaining);