2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
365 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367 return Roo.get(document.body);
371 * Fetch the element to display the tooltip on.
372 * @return {Roo.Element} defaults to this.el
374 tooltipEl : function()
379 addxtype : function(tree,cntr)
383 cn = Roo.factory(tree);
384 //Roo.log(['addxtype', cn]);
386 cn.parentType = this.xtype; //??
387 cn.parentId = this.id;
389 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390 if (typeof(cn.container_method) == 'string') {
391 cntr = cn.container_method;
395 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
397 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
399 var build_from_html = Roo.XComponent.build_from_html;
401 var is_body = (tree.xtype == 'Body') ;
403 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405 var self_cntr_el = Roo.get(this[cntr](false));
407 // do not try and build conditional elements
408 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414 return this.addxtypeChild(tree,cntr, is_body);
417 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
420 return this.addxtypeChild(Roo.apply({}, tree),cntr);
423 Roo.log('skipping render');
429 if (!build_from_html) {
433 // this i think handles overlaying multiple children of the same type
434 // with the sam eelement.. - which might be buggy..
436 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
442 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453 addxtypeChild : function (tree, cntr, is_body)
455 Roo.debug && Roo.log('addxtypeChild:' + cntr);
457 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
460 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461 (typeof(tree['flexy:foreach']) != 'undefined');
465 skip_children = false;
466 // render the element if it's not BODY.
469 // if parent was disabled, then do not try and create the children..
470 if(!this[cntr](true)){
475 cn = Roo.factory(tree);
477 cn.parentType = this.xtype; //??
478 cn.parentId = this.id;
480 var build_from_html = Roo.XComponent.build_from_html;
483 // does the container contain child eleemnts with 'xtype' attributes.
484 // that match this xtype..
485 // note - when we render we create these as well..
486 // so we should check to see if body has xtype set.
487 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489 var self_cntr_el = Roo.get(this[cntr](false));
490 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492 //Roo.log(Roo.XComponent.build_from_html);
493 //Roo.log("got echild:");
496 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497 // and are not displayed -this causes this to use up the wrong element when matching.
498 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
501 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
508 //echild.dom.removeAttribute('xtype');
510 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511 Roo.debug && Roo.log(self_cntr_el);
512 Roo.debug && Roo.log(echild);
513 Roo.debug && Roo.log(cn);
519 // if object has flexy:if - then it may or may not be rendered.
520 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
521 // skip a flexy if element.
522 Roo.debug && Roo.log('skipping render');
523 Roo.debug && Roo.log(tree);
525 Roo.debug && Roo.log('skipping all children');
526 skip_children = true;
531 // actually if flexy:foreach is found, we really want to create
532 // multiple copies here...
534 //Roo.log(this[cntr]());
535 // some elements do not have render methods.. like the layouts...
537 if(this[cntr](true) === false){
542 cn.render && cn.render(this[cntr](true));
545 // then add the element..
552 if (typeof (tree.menu) != 'undefined') {
553 tree.menu.parentType = cn.xtype;
554 tree.menu.triggerEl = cn.el;
555 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559 if (!tree.items || !tree.items.length) {
561 //Roo.log(["no children", this]);
566 var items = tree.items;
569 //Roo.log(items.length);
571 if (!skip_children) {
572 for(var i =0;i < items.length;i++) {
573 // Roo.log(['add child', items[i]]);
574 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
580 //Roo.log("fire childrenrendered");
582 cn.fireEvent('childrenrendered', this);
588 * Set the element that will be used to show or hide
590 setVisibilityEl : function(el)
592 this.visibilityEl = el;
596 * Get the element that will be used to show or hide
598 getVisibilityEl : function()
600 if (typeof(this.visibilityEl) == 'object') {
601 return this.visibilityEl;
604 if (typeof(this.visibilityEl) == 'string') {
605 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612 * Show a component - removes 'hidden' class
616 if(!this.getVisibilityEl()){
620 this.getVisibilityEl().removeClass(['hidden','d-none']);
622 this.fireEvent('show', this);
627 * Hide a component - adds 'hidden' class
631 if(!this.getVisibilityEl()){
635 this.getVisibilityEl().addClass(['hidden','d-none']);
637 this.fireEvent('hide', this);
650 * @class Roo.bootstrap.Element
651 * @extends Roo.bootstrap.Component
652 * Bootstrap Element class
653 * @cfg {String} html contents of the element
654 * @cfg {String} tag tag of the element
655 * @cfg {String} cls class of the element
656 * @cfg {Boolean} preventDefault (true|false) default false
657 * @cfg {Boolean} clickable (true|false) default false
658 * @cfg {String} role default blank - set to button to force cursor pointer
662 * Create a new Element
663 * @param {Object} config The config object
666 Roo.bootstrap.Element = function(config){
667 Roo.bootstrap.Element.superclass.constructor.call(this, config);
673 * When a element is chick
674 * @param {Roo.bootstrap.Element} this
675 * @param {Roo.EventObject} e
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
688 preventDefault: false,
693 getAutoCreate : function(){
697 // cls: this.cls, double assign in parent class Component.js :: onRender
700 if (this.role !== false) {
701 cfg.role = this.role;
707 initEvents: function()
709 Roo.bootstrap.Element.superclass.initEvents.call(this);
712 this.el.on('click', this.onClick, this);
718 onClick : function(e)
720 if(this.preventDefault){
724 this.fireEvent('click', this, e); // why was this double click before?
732 getValue : function()
734 return this.el.dom.innerHTML;
737 setValue : function(value)
739 this.el.dom.innerHTML = value;
754 * @class Roo.bootstrap.DropTarget
755 * @extends Roo.bootstrap.Element
756 * Bootstrap DropTarget class
758 * @cfg {string} name dropable name
761 * Create a new Dropable Area
762 * @param {Object} config The config object
765 Roo.bootstrap.DropTarget = function(config){
766 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772 * When a element is chick
773 * @param {Roo.bootstrap.Element} this
774 * @param {Roo.EventObject} e
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
783 getAutoCreate : function(){
788 initEvents: function()
790 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
794 drop : this.dragDrop.createDelegate(this),
795 enter : this.dragEnter.createDelegate(this),
796 out : this.dragOut.createDelegate(this),
797 over : this.dragOver.createDelegate(this)
801 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
804 dragDrop : function(source,e,data)
806 // user has to decide how to impliment this.
809 //this.fireEvent('drop', this, source, e ,data);
813 dragEnter : function(n, dd, e, data)
815 // probably want to resize the element to match the dropped element..
817 this.originalSize = this.el.getSize();
818 this.el.setSize( n.el.getSize());
819 this.dropZone.DDM.refreshCache(this.name);
820 Roo.log([n, dd, e, data]);
823 dragOut : function(value)
825 // resize back to normal
827 this.el.setSize(this.originalSize);
828 this.dropZone.resetConstraints();
831 dragOver : function()
848 * @class Roo.bootstrap.Body
849 * @extends Roo.bootstrap.Component
850 * Bootstrap Body class
854 * @param {Object} config The config object
857 Roo.bootstrap.Body = function(config){
859 config = config || {};
861 Roo.bootstrap.Body.superclass.constructor.call(this, config);
862 this.el = Roo.get(config.el ? config.el : document.body );
863 if (this.cls && this.cls.length) {
864 Roo.get(document.body).addClass(this.cls);
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
870 is_body : true,// just to make sure it's constructed?
875 onRender : function(ct, position)
877 /* Roo.log("Roo.bootstrap.Body - onRender");
878 if (this.cls && this.cls.length) {
879 Roo.get(document.body).addClass(this.cls);
898 * @class Roo.bootstrap.ButtonGroup
899 * @extends Roo.bootstrap.Component
900 * Bootstrap ButtonGroup class
901 * @cfg {String} size lg | sm | xs (default empty normal)
902 * @cfg {String} align vertical | justified (default none)
903 * @cfg {String} direction up | down (default down)
904 * @cfg {Boolean} toolbar false | true
905 * @cfg {Boolean} btn true | false
910 * @param {Object} config The config object
913 Roo.bootstrap.ButtonGroup = function(config){
914 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
925 getAutoCreate : function(){
931 cfg.html = this.html || cfg.html;
942 if (['vertical','justified'].indexOf(this.align)!==-1) {
943 cfg.cls = 'btn-group-' + this.align;
945 if (this.align == 'justified') {
946 console.log(this.items);
950 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951 cfg.cls += ' btn-group-' + this.size;
954 if (this.direction == 'up') {
955 cfg.cls += ' dropup' ;
961 * Add a button to the group (similar to NavItem API.)
963 addItem : function(cfg)
965 var cn = new Roo.bootstrap.Button(cfg);
967 cn.parentId = this.id;
968 cn.onRender(this.el, null);
982 * @class Roo.bootstrap.Button
983 * @extends Roo.bootstrap.Component
984 * Bootstrap Button class
985 * @cfg {String} html The button content
986 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989 * @cfg {String} size (lg|sm|xs)
990 * @cfg {String} tag (a|input|submit)
991 * @cfg {String} href empty or href
992 * @cfg {Boolean} disabled default false;
993 * @cfg {Boolean} isClose default false;
994 * @cfg {String} glyphicon depricated - use fa
995 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996 * @cfg {String} badge text for badge
997 * @cfg {String} theme (default|glow)
998 * @cfg {Boolean} inverse dark themed version
999 * @cfg {Boolean} toggle is it a slidy toggle button
1000 * @cfg {Boolean} pressed default null - if the button ahs active state
1001 * @cfg {String} ontext text for on slidy toggle state
1002 * @cfg {String} offtext text for off slidy toggle state
1003 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1004 * @cfg {Boolean} removeClass remove the standard class..
1005 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1006 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1009 * Create a new button
1010 * @param {Object} config The config object
1014 Roo.bootstrap.Button = function(config){
1015 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1021 * When a button is pressed
1022 * @param {Roo.bootstrap.Button} btn
1023 * @param {Roo.EventObject} e
1028 * When a button is double clicked
1029 * @param {Roo.bootstrap.Button} btn
1030 * @param {Roo.EventObject} e
1035 * After the button has been toggles
1036 * @param {Roo.bootstrap.Button} btn
1037 * @param {Roo.EventObject} e
1038 * @param {boolean} pressed (also available as button.pressed)
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1065 preventDefault: true,
1074 getAutoCreate : function(){
1082 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084 this.tag = 'button';
1088 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090 if (this.toggle == true) {
1093 cls: 'slider-frame roo-button',
1097 'data-on-text':'ON',
1098 'data-off-text':'OFF',
1099 cls: 'slider-button',
1104 // why are we validating the weights?
1105 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106 cfg.cls += ' ' + this.weight;
1113 cfg.cls += ' close';
1115 cfg["aria-hidden"] = true;
1117 cfg.html = "×";
1123 if (this.theme==='default') {
1124 cfg.cls = 'btn roo-button';
1126 //if (this.parentType != 'Navbar') {
1127 this.weight = this.weight.length ? this.weight : 'default';
1129 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133 cfg.cls += ' btn-' + outline + weight;
1134 if (this.weight == 'default') {
1136 cfg.cls += ' btn-' + this.weight;
1139 } else if (this.theme==='glow') {
1142 cfg.cls = 'btn-glow roo-button';
1144 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146 cfg.cls += ' ' + this.weight;
1152 this.cls += ' inverse';
1156 if (this.active || this.pressed === true) {
1157 cfg.cls += ' active';
1160 if (this.disabled) {
1161 cfg.disabled = 'disabled';
1165 Roo.log('changing to ul' );
1167 this.glyphicon = 'caret';
1168 if (Roo.bootstrap.version == 4) {
1169 this.fa = 'caret-down';
1174 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176 //gsRoo.log(this.parentType);
1177 if (this.parentType === 'Navbar' && !this.parent().bar) {
1178 Roo.log('changing to li?');
1187 href : this.href || '#'
1190 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1191 cfg.cls += ' dropdown';
1198 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1200 if (this.glyphicon) {
1201 cfg.html = ' ' + cfg.html;
1206 cls: 'glyphicon glyphicon-' + this.glyphicon
1211 cfg.html = ' ' + cfg.html;
1216 cls: 'fa fas fa-' + this.fa
1226 // cfg.cls='btn roo-button';
1230 var value = cfg.html;
1235 cls: 'glyphicon glyphicon-' + this.glyphicon,
1242 cls: 'fa fas fa-' + this.fa,
1247 var bw = this.badge_weight.length ? this.badge_weight :
1248 (this.weight.length ? this.weight : 'secondary');
1249 bw = bw == 'default' ? 'secondary' : bw;
1255 cls: 'badge badge-' + bw,
1264 cfg.cls += ' dropdown';
1265 cfg.html = typeof(cfg.html) != 'undefined' ?
1266 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1269 if (cfg.tag !== 'a' && this.href !== '') {
1270 throw "Tag must be a to set href.";
1271 } else if (this.href.length > 0) {
1272 cfg.href = this.href;
1275 if(this.removeClass){
1280 cfg.target = this.target;
1285 initEvents: function() {
1286 // Roo.log('init events?');
1287 // Roo.log(this.el.dom);
1290 if (typeof (this.menu) != 'undefined') {
1291 this.menu.parentType = this.xtype;
1292 this.menu.triggerEl = this.el;
1293 this.addxtype(Roo.apply({}, this.menu));
1297 if (this.el.hasClass('roo-button')) {
1298 this.el.on('click', this.onClick, this);
1299 this.el.on('dblclick', this.onDblClick, this);
1301 this.el.select('.roo-button').on('click', this.onClick, this);
1302 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1306 if(this.removeClass){
1307 this.el.on('click', this.onClick, this);
1310 if (this.group === true) {
1311 if (this.pressed === false || this.pressed === true) {
1314 this.pressed = false;
1315 this.setActive(this.pressed);
1320 this.el.enableDisplayMode();
1323 onClick : function(e)
1325 if (this.disabled) {
1329 Roo.log('button on click ');
1330 if(this.preventDefault){
1339 this.setActive(true);
1340 var pi = this.parent().items;
1341 for (var i = 0;i < pi.length;i++) {
1342 if (this == pi[i]) {
1345 if (pi[i].el.hasClass('roo-button')) {
1346 pi[i].setActive(false);
1349 this.fireEvent('click', this, e);
1353 if (this.pressed === true || this.pressed === false) {
1354 this.toggleActive(e);
1358 this.fireEvent('click', this, e);
1360 onDblClick: function(e)
1362 if (this.disabled) {
1365 if(this.preventDefault){
1368 this.fireEvent('dblclick', this, e);
1371 * Enables this button
1375 this.disabled = false;
1376 this.el.removeClass('disabled');
1377 this.el.dom.removeAttribute("disabled");
1381 * Disable this button
1383 disable : function()
1385 this.disabled = true;
1386 this.el.addClass('disabled');
1387 this.el.attr("disabled", "disabled")
1390 * sets the active state on/off,
1391 * @param {Boolean} state (optional) Force a particular state
1393 setActive : function(v) {
1395 this.el[v ? 'addClass' : 'removeClass']('active');
1399 * toggles the current active state
1401 toggleActive : function(e)
1403 this.setActive(!this.pressed); // this modifies pressed...
1404 this.fireEvent('toggle', this, e, this.pressed);
1407 * get the current active state
1408 * @return {boolean} true if it's active
1410 isActive : function()
1412 return this.el.hasClass('active');
1415 * set the text of the first selected button
1417 setText : function(str)
1419 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1422 * get the text of the first selected button
1424 getText : function()
1426 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1429 setWeight : function(str)
1431 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434 var outline = this.outline ? 'outline-' : '';
1435 if (str == 'default') {
1436 this.el.addClass('btn-default btn-outline-secondary');
1439 this.el.addClass('btn-' + outline + str);
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446 Roo.bootstrap.Button.weights = [
1466 * @class Roo.bootstrap.Column
1467 * @extends Roo.bootstrap.Component
1468 * Bootstrap Column class
1469 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1479 * @cfg {Boolean} hidden (true|false) hide the element
1480 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481 * @cfg {String} fa (ban|check|...) font awesome icon
1482 * @cfg {Number} fasize (1|2|....) font awsome size
1484 * @cfg {String} icon (info-sign|check|...) glyphicon name
1486 * @cfg {String} html content of column.
1489 * Create a new Column
1490 * @param {Object} config The config object
1493 Roo.bootstrap.Column = function(config){
1494 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1515 getAutoCreate : function(){
1516 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1524 var sizes = ['xs','sm','md','lg'];
1525 sizes.map(function(size ,ix){
1526 //Roo.log( size + ':' + settings[size]);
1528 if (settings[size+'off'] !== false) {
1529 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1532 if (settings[size] === false) {
1536 if (!settings[size]) { // 0 = hidden
1537 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539 for (var i = ix; i > -1; i--) {
1540 cfg.cls += ' d-' + sizes[i] + '-none';
1546 cfg.cls += ' col-' + size + '-' + settings[size] + (
1547 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1553 cfg.cls += ' hidden';
1556 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557 cfg.cls +=' alert alert-' + this.alert;
1561 if (this.html.length) {
1562 cfg.html = this.html;
1566 if (this.fasize > 1) {
1567 fasize = ' fa-' + this.fasize + 'x';
1569 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1574 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1593 * @class Roo.bootstrap.Container
1594 * @extends Roo.bootstrap.Component
1595 * Bootstrap Container class
1596 * @cfg {Boolean} jumbotron is it a jumbotron element
1597 * @cfg {String} html content of element
1598 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1600 * @cfg {String} header content of header (for panel)
1601 * @cfg {String} footer content of footer (for panel)
1602 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603 * @cfg {String} tag (header|aside|section) type of HTML tag.
1604 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605 * @cfg {String} fa font awesome icon
1606 * @cfg {String} icon (info-sign|check|...) glyphicon name
1607 * @cfg {Boolean} hidden (true|false) hide the element
1608 * @cfg {Boolean} expandable (true|false) default false
1609 * @cfg {Boolean} expanded (true|false) default true
1610 * @cfg {String} rheader contet on the right of header
1611 * @cfg {Boolean} clickable (true|false) default false
1615 * Create a new Container
1616 * @param {Object} config The config object
1619 Roo.bootstrap.Container = function(config){
1620 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1626 * After the panel has been expand
1628 * @param {Roo.bootstrap.Container} this
1633 * After the panel has been collapsed
1635 * @param {Roo.bootstrap.Container} this
1640 * When a element is chick
1641 * @param {Roo.bootstrap.Container} this
1642 * @param {Roo.EventObject} e
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1666 getChildContainer : function() {
1672 if (this.panel.length) {
1673 return this.el.select('.panel-body',true).first();
1680 getAutoCreate : function(){
1683 tag : this.tag || 'div',
1687 if (this.jumbotron) {
1688 cfg.cls = 'jumbotron';
1693 // - this is applied by the parent..
1695 // cfg.cls = this.cls + '';
1698 if (this.sticky.length) {
1700 var bd = Roo.get(document.body);
1701 if (!bd.hasClass('bootstrap-sticky')) {
1702 bd.addClass('bootstrap-sticky');
1703 Roo.select('html',true).setStyle('height', '100%');
1706 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1710 if (this.well.length) {
1711 switch (this.well) {
1714 cfg.cls +=' well well-' +this.well;
1723 cfg.cls += ' hidden';
1727 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728 cfg.cls +=' alert alert-' + this.alert;
1733 if (this.panel.length) {
1734 cfg.cls += ' panel panel-' + this.panel;
1736 if (this.header.length) {
1740 if(this.expandable){
1742 cfg.cls = cfg.cls + ' expandable';
1746 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1754 cls : 'panel-title',
1755 html : (this.expandable ? ' ' : '') + this.header
1759 cls: 'panel-header-right',
1765 cls : 'panel-heading',
1766 style : this.expandable ? 'cursor: pointer' : '',
1774 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1779 if (this.footer.length) {
1781 cls : 'panel-footer',
1790 body.html = this.html || cfg.html;
1791 // prefix with the icons..
1793 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1796 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1801 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802 cfg.cls = 'container';
1808 initEvents: function()
1810 if(this.expandable){
1811 var headerEl = this.headerEl();
1814 headerEl.on('click', this.onToggleClick, this);
1819 this.el.on('click', this.onClick, this);
1824 onToggleClick : function()
1826 var headerEl = this.headerEl();
1842 if(this.fireEvent('expand', this)) {
1844 this.expanded = true;
1846 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848 this.el.select('.panel-body',true).first().removeClass('hide');
1850 var toggleEl = this.toggleEl();
1856 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1861 collapse : function()
1863 if(this.fireEvent('collapse', this)) {
1865 this.expanded = false;
1867 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868 this.el.select('.panel-body',true).first().addClass('hide');
1870 var toggleEl = this.toggleEl();
1876 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1880 toggleEl : function()
1882 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1886 return this.el.select('.panel-heading .fa',true).first();
1889 headerEl : function()
1891 if(!this.el || !this.panel.length || !this.header.length){
1895 return this.el.select('.panel-heading',true).first()
1900 if(!this.el || !this.panel.length){
1904 return this.el.select('.panel-body',true).first()
1907 titleEl : function()
1909 if(!this.el || !this.panel.length || !this.header.length){
1913 return this.el.select('.panel-title',true).first();
1916 setTitle : function(v)
1918 var titleEl = this.titleEl();
1924 titleEl.dom.innerHTML = v;
1927 getTitle : function()
1930 var titleEl = this.titleEl();
1936 return titleEl.dom.innerHTML;
1939 setRightTitle : function(v)
1941 var t = this.el.select('.panel-header-right',true).first();
1947 t.dom.innerHTML = v;
1950 onClick : function(e)
1954 this.fireEvent('click', this, e);
1961 * This is BS4's Card element.. - similar to our containers probably..
1965 * @class Roo.bootstrap.Card
1966 * @extends Roo.bootstrap.Component
1967 * Bootstrap Card class
1970 * possible... may not be implemented..
1971 * @cfg {String} header_image src url of image.
1972 * @cfg {String|Object} header
1973 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1976 * @cfg {String} title
1977 * @cfg {String} subtitle
1978 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979 * @cfg {String} footer
1981 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983 * @cfg {String} margin (0|1|2|3|4|5|auto)
1984 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991 * @cfg {String} padding (0|1|2|3|4|5)
1992 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994 * @cfg {String} padding_left (0|1|2|3|4|5)
1995 * @cfg {String} padding_right (0|1|2|3|4|5)
1996 * @cfg {String} padding_x (0|1|2|3|4|5)
1997 * @cfg {String} padding_y (0|1|2|3|4|5)
1999 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @config {Boolean} dragable if this card can be dragged.
2006 * @config {String} drag_group group for drag
2007 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2008 * @config {String} drop_group group for drag
2010 * @config {Boolean} collapsable can the body be collapsed.
2011 * @config {Boolean} collapsed is the body collapsed when rendered...
2012 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013 * @config {Boolean} rotated is the body rotated when rendered...
2016 * Create a new Container
2017 * @param {Object} config The config object
2020 Roo.bootstrap.Card = function(config){
2021 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2027 * When a element a card is dropped
2028 * @param {Roo.bootstrap.Card} this
2031 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032 * @param {String} position 'above' or 'below'
2033 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2039 * When a element a card is rotate
2040 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.Element} n the node being dropped?
2042 * @param {Boolean} rotate status
2047 * When a card element is dragged over ready to drop (return false to block dropable)
2048 * @param {Roo.bootstrap.Card} this
2049 * @param {Object} data from dragdrop
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2062 margin: '', /// may be better in component?
2092 collapsable : false,
2101 childContainer : false,
2102 dropEl : false, /// the dom placeholde element that indicates drop location.
2103 containerEl: false, // body container
2104 bodyEl: false, // card-body
2105 headerContainerEl : false, //
2107 header_imageEl : false,
2110 layoutCls : function()
2114 Roo.log(this.margin_bottom.length);
2115 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2121 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2126 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2132 // more generic support?
2140 // Roo.log("Call onRender: " + this.xtype);
2141 /* We are looking at something like this.
2143 <img src="..." class="card-img-top" alt="...">
2144 <div class="card-body">
2145 <h5 class="card-title">Card title</h5>
2146 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148 >> this bit is really the body...
2149 <div> << we will ad dthis in hopefully it will not break shit.
2151 ** card text does not actually have any styling...
2153 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2156 <a href="#" class="card-link">Card link</a>
2159 <div class="card-footer">
2160 <small class="text-muted">Last updated 3 mins ago</small>
2164 getAutoCreate : function(){
2172 if (this.weight.length && this.weight != 'light') {
2173 cfg.cls += ' text-white';
2175 cfg.cls += ' text-dark'; // need as it's nested..
2177 if (this.weight.length) {
2178 cfg.cls += ' bg-' + this.weight;
2181 cfg.cls += ' ' + this.layoutCls();
2184 var hdr_ctr = false;
2185 if (this.header.length) {
2187 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2196 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202 if (this.collapsable) {
2205 cls : 'd-block user-select-none',
2209 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2214 hdr.cn.push(hdr_ctr);
2219 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2224 if (this.header_image.length) {
2227 cls : 'card-img-top',
2228 src: this.header_image // escape?
2233 cls : 'card-img-top d-none'
2239 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2243 if (this.collapsable || this.rotateable) {
2246 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2253 if (this.title.length) {
2257 src: this.title // escape?
2261 if (this.subtitle.length) {
2265 src: this.subtitle // escape?
2271 cls : 'roo-card-body-ctr'
2274 if (this.html.length) {
2280 // fixme ? handle objects?
2282 if (this.footer.length) {
2285 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2290 cfg.cn.push({cls : 'card-footer d-none'});
2299 getCardHeader : function()
2301 var ret = this.el.select('.card-header',true).first();
2302 if (ret.hasClass('d-none')) {
2303 ret.removeClass('d-none');
2308 getCardFooter : function()
2310 var ret = this.el.select('.card-footer',true).first();
2311 if (ret.hasClass('d-none')) {
2312 ret.removeClass('d-none');
2317 getCardImageTop : function()
2319 var ret = this.header_imageEl;
2320 if (ret.hasClass('d-none')) {
2321 ret.removeClass('d-none');
2327 getChildContainer : function()
2333 return this.el.select('.roo-card-body-ctr',true).first();
2336 initEvents: function()
2338 this.bodyEl = this.el.select('.card-body',true).first();
2339 this.containerEl = this.getChildContainer();
2341 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342 containerScroll: true,
2343 ddGroup: this.drag_group || 'default_card_drag_group'
2345 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347 if (this.dropable) {
2348 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349 containerScroll: true,
2350 ddGroup: this.drop_group || 'default_card_drag_group'
2352 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2359 if (this.collapsable) {
2360 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362 if (this.rotateable) {
2363 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367 this.footerEl = this.el.select('.card-footer',true).first();
2368 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370 this.headerEl = this.el.select('.card-header',true).first();
2373 this.el.addClass('roo-card-rotated');
2374 this.fireEvent('rotate', this, true);
2376 this.header_imageEl = this.el.select('.card-img-top',true).first();
2377 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2380 getDragData : function(e)
2382 var target = this.getEl();
2384 //this.handleSelection(e);
2389 nodes: this.getEl(),
2394 dragData.ddel = target.dom ; // the div element
2395 Roo.log(target.getWidth( ));
2396 dragData.ddel.style.width = target.getWidth() + 'px';
2403 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2404 * whole Element becomes the target, and this causes the drop gesture to append.
2406 * Returns an object:
2409 position : 'below' or 'above'
2410 card : relateive to card OBJECT (or true for no cards listed)
2411 items_n : relative to nth item in list
2412 card_n : relative to nth card in list
2417 getTargetFromEvent : function(e, dragged_card_el)
2419 var target = e.getTarget();
2420 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421 target = target.parentNode;
2432 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433 // see if target is one of the 'cards'...
2436 //Roo.log(this.items.length);
2439 var last_card_n = 0;
2441 for (var i = 0;i< this.items.length;i++) {
2443 if (!this.items[i].el.hasClass('card')) {
2446 pos = this.getDropPoint(e, this.items[i].el.dom);
2448 cards_len = ret.cards.length;
2449 //Roo.log(this.items[i].el.dom.id);
2450 ret.cards.push(this.items[i]);
2452 if (ret.card_n < 0 && pos == 'above') {
2453 ret.position = cards_len > 0 ? 'below' : pos;
2454 ret.items_n = i > 0 ? i - 1 : 0;
2455 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2456 ret.card = ret.cards[ret.card_n];
2459 if (!ret.cards.length) {
2461 ret.position = 'below';
2465 // could not find a card.. stick it at the end..
2466 if (ret.card_n < 0) {
2467 ret.card_n = last_card_n;
2468 ret.card = ret.cards[last_card_n];
2469 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470 ret.position = 'below';
2473 if (this.items[ret.items_n].el == dragged_card_el) {
2477 if (ret.position == 'below') {
2478 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480 if (card_after && card_after.el == dragged_card_el) {
2487 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489 if (card_before && card_before.el == dragged_card_el) {
2496 onNodeEnter : function(n, dd, e, data){
2499 onNodeOver : function(n, dd, e, data)
2502 var target_info = this.getTargetFromEvent(e,data.source.el);
2503 if (target_info === false) {
2504 this.dropPlaceHolder('hide');
2507 Roo.log(['getTargetFromEvent', target_info ]);
2510 if (this.fireEvent('cardover', this, [ data ]) === false) {
2514 this.dropPlaceHolder('show', target_info,data);
2518 onNodeOut : function(n, dd, e, data){
2519 this.dropPlaceHolder('hide');
2522 onNodeDrop : function(n, dd, e, data)
2525 // call drop - return false if
2527 // this could actually fail - if the Network drops..
2528 // we will ignore this at present..- client should probably reload
2529 // the whole set of cards if stuff like that fails.
2532 var info = this.getTargetFromEvent(e,data.source.el);
2533 if (info === false) {
2536 this.dropPlaceHolder('hide');
2540 this.acceptCard(data.source, info.position, info.card, info.items_n);
2544 firstChildCard : function()
2546 for (var i = 0;i< this.items.length;i++) {
2548 if (!this.items[i].el.hasClass('card')) {
2551 return this.items[i];
2553 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2558 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2560 acceptCard : function(move_card, position, next_to_card )
2562 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2566 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568 move_card.parent().removeCard(move_card);
2571 var dom = move_card.el.dom;
2572 dom.style.width = ''; // clear with - which is set by drag.
2574 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575 var cardel = next_to_card.el.dom;
2577 if (position == 'above' ) {
2578 cardel.parentNode.insertBefore(dom, cardel);
2579 } else if (cardel.nextSibling) {
2580 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582 cardel.parentNode.append(dom);
2585 // card container???
2586 this.containerEl.dom.append(dom);
2589 //FIXME HANDLE card = true
2591 // add this to the correct place in items.
2593 // remove Card from items.
2596 if (this.items.length) {
2598 //Roo.log([info.items_n, info.position, this.items.length]);
2599 for (var i =0; i < this.items.length; i++) {
2600 if (i == to_items_n && position == 'above') {
2601 nitems.push(move_card);
2603 nitems.push(this.items[i]);
2604 if (i == to_items_n && position == 'below') {
2605 nitems.push(move_card);
2608 this.items = nitems;
2609 Roo.log(this.items);
2611 this.items.push(move_card);
2614 move_card.parentId = this.id;
2620 removeCard : function(c)
2622 this.items = this.items.filter(function(e) { return e != c });
2625 dom.parentNode.removeChild(dom);
2626 dom.style.width = ''; // clear with - which is set by drag.
2631 /** Decide whether to drop above or below a View node. */
2632 getDropPoint : function(e, n, dd)
2637 if (n == this.containerEl.dom) {
2640 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641 var c = t + (b - t) / 2;
2642 var y = Roo.lib.Event.getPageY(e);
2649 onToggleCollapse : function(e)
2651 if (this.collapsed) {
2652 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653 this.collapsableEl.addClass('show');
2654 this.collapsed = false;
2657 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658 this.collapsableEl.removeClass('show');
2659 this.collapsed = true;
2664 onToggleRotate : function(e)
2666 this.collapsableEl.removeClass('show');
2667 this.footerEl.removeClass('d-none');
2668 this.el.removeClass('roo-card-rotated');
2669 this.el.removeClass('d-none');
2672 this.collapsableEl.addClass('show');
2673 this.rotated = false;
2674 this.fireEvent('rotate', this, this.rotated);
2677 this.el.addClass('roo-card-rotated');
2678 this.footerEl.addClass('d-none');
2679 this.el.select('.roo-collapsable').removeClass('show');
2681 this.rotated = true;
2682 this.fireEvent('rotate', this, this.rotated);
2686 dropPlaceHolder: function (action, info, data)
2688 if (this.dropEl === false) {
2689 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2693 this.dropEl.removeClass(['d-none', 'd-block']);
2694 if (action == 'hide') {
2696 this.dropEl.addClass('d-none');
2699 // FIXME - info.card == true!!!
2700 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702 if (info.card !== true) {
2703 var cardel = info.card.el.dom;
2705 if (info.position == 'above') {
2706 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707 } else if (cardel.nextSibling) {
2708 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710 cardel.parentNode.append(this.dropEl.dom);
2713 // card container???
2714 this.containerEl.dom.append(this.dropEl.dom);
2717 this.dropEl.addClass('d-block roo-card-dropzone');
2719 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2726 setHeaderText: function(html)
2729 if (this.headerContainerEl) {
2730 this.headerContainerEl.dom.innerHTML = html;
2733 onHeaderImageLoad : function(ev, he)
2735 if (!this.header_image_fit_square) {
2739 var hw = he.naturalHeight / he.naturalWidth;
2742 //var w = he.dom.naturalWidth;
2745 he.style.position = 'relative';
2747 var nw = (ww * (1/hw));
2748 Roo.get(he).setSize( ww * (1/hw), ww);
2749 he.style.left = ((ww - nw)/ 2) + 'px';
2750 he.style.position = 'relative';
2761 * Card header - holder for the card header elements.
2766 * @class Roo.bootstrap.CardHeader
2767 * @extends Roo.bootstrap.Element
2768 * Bootstrap CardHeader class
2770 * Create a new Card Header - that you can embed children into
2771 * @param {Object} config The config object
2774 Roo.bootstrap.CardHeader = function(config){
2775 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2781 container_method : 'getCardHeader'
2794 * Card footer - holder for the card footer elements.
2799 * @class Roo.bootstrap.CardFooter
2800 * @extends Roo.bootstrap.Element
2801 * Bootstrap CardFooter class
2803 * Create a new Card Footer - that you can embed children into
2804 * @param {Object} config The config object
2807 Roo.bootstrap.CardFooter = function(config){
2808 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2814 container_method : 'getCardFooter'
2827 * Card header - holder for the card header elements.
2832 * @class Roo.bootstrap.CardImageTop
2833 * @extends Roo.bootstrap.Element
2834 * Bootstrap CardImageTop class
2836 * Create a new Card Image Top container
2837 * @param {Object} config The config object
2840 Roo.bootstrap.CardImageTop = function(config){
2841 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2847 container_method : 'getCardImageTop'
2862 * @class Roo.bootstrap.ButtonUploader
2863 * @extends Roo.bootstrap.Button
2864 * Bootstrap Button Uploader class - it's a button which when you add files to it
2867 * @cfg {Number} errorTimeout default 3000
2868 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2869 * @cfg {Array} html The button text.
2870 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2873 * Create a new CardUploader
2874 * @param {Object} config The config object
2877 Roo.bootstrap.ButtonUploader = function(config){
2881 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2887 * @event beforeselect
2888 * When button is pressed, before show upload files dialog is shown
2889 * @param {Roo.bootstrap.UploaderButton} this
2892 'beforeselect' : true,
2894 * @event fired when files have been selected,
2895 * When a the download link is clicked
2896 * @param {Roo.bootstrap.UploaderButton} this
2897 * @param {Array} Array of files that have been uploaded
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2907 errorTimeout : 3000,
2911 fileCollection : false,
2916 getAutoCreate : function()
2921 cls : 'd-none roo-card-upload-selector'
2924 if (this.multiple) {
2925 im.multiple = 'multiple';
2931 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2941 initEvents : function()
2944 Roo.bootstrap.Button.prototype.initEvents.call(this);
2950 this.urlAPI = (window.createObjectURL && window) ||
2951 (window.URL && URL.revokeObjectURL && URL) ||
2952 (window.webkitURL && webkitURL);
2957 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959 this.selectorEl.on('change', this.onFileSelected, this);
2966 onClick : function(e)
2970 if ( this.fireEvent('beforeselect', this) === false) {
2974 this.selectorEl.dom.click();
2978 onFileSelected : function(e)
2982 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2985 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986 this.selectorEl.dom.value = '';// hopefully reset..
2988 this.fireEvent('uploaded', this, files );
2996 * addCard - add an Attachment to the uploader
2997 * @param data - the data about the image to upload
3001 title : "Title of file",
3002 is_uploaded : false,
3003 src : "http://.....",
3004 srcfile : { the File upload object },
3005 mimetype : file.type,
3008 .. any other data...
3033 * @class Roo.bootstrap.Img
3034 * @extends Roo.bootstrap.Component
3035 * Bootstrap Img class
3036 * @cfg {Boolean} imgResponsive false | true
3037 * @cfg {String} border rounded | circle | thumbnail
3038 * @cfg {String} src image source
3039 * @cfg {String} alt image alternative text
3040 * @cfg {String} href a tag href
3041 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042 * @cfg {String} xsUrl xs image source
3043 * @cfg {String} smUrl sm image source
3044 * @cfg {String} mdUrl md image source
3045 * @cfg {String} lgUrl lg image source
3048 * Create a new Input
3049 * @param {Object} config The config object
3052 Roo.bootstrap.Img = function(config){
3053 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3059 * The img click event for the img.
3060 * @param {Roo.EventObject} e
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3068 imgResponsive: true,
3078 getAutoCreate : function()
3080 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081 return this.createSingleImg();
3086 cls: 'roo-image-responsive-group',
3091 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3093 if(!_this[size + 'Url']){
3099 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100 html: _this.html || cfg.html,
3101 src: _this[size + 'Url']
3104 img.cls += ' roo-image-responsive-' + size;
3106 var s = ['xs', 'sm', 'md', 'lg'];
3108 s.splice(s.indexOf(size), 1);
3110 Roo.each(s, function(ss){
3111 img.cls += ' hidden-' + ss;
3114 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115 cfg.cls += ' img-' + _this.border;
3119 cfg.alt = _this.alt;
3132 a.target = _this.target;
3136 cfg.cn.push((_this.href) ? a : img);
3143 createSingleImg : function()
3147 cls: (this.imgResponsive) ? 'img-responsive' : '',
3149 src : 'about:blank' // just incase src get's set to undefined?!?
3152 cfg.html = this.html || cfg.html;
3154 cfg.src = this.src || cfg.src;
3156 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157 cfg.cls += ' img-' + this.border;
3174 a.target = this.target;
3179 return (this.href) ? a : cfg;
3182 initEvents: function()
3185 this.el.on('click', this.onClick, this);
3190 onClick : function(e)
3192 Roo.log('img onclick');
3193 this.fireEvent('click', this, e);
3196 * Sets the url of the image - used to update it
3197 * @param {String} url the url of the image
3200 setSrc : function(url)
3204 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205 this.el.dom.src = url;
3209 this.el.select('img', true).first().dom.src = url;
3225 * @class Roo.bootstrap.Link
3226 * @extends Roo.bootstrap.Component
3227 * Bootstrap Link Class
3228 * @cfg {String} alt image alternative text
3229 * @cfg {String} href a tag href
3230 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231 * @cfg {String} html the content of the link.
3232 * @cfg {String} anchor name for the anchor link
3233 * @cfg {String} fa - favicon
3235 * @cfg {Boolean} preventDefault (true | false) default false
3239 * Create a new Input
3240 * @param {Object} config The config object
3243 Roo.bootstrap.Link = function(config){
3244 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3250 * The img click event for the img.
3251 * @param {Roo.EventObject} e
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3261 preventDefault: false,
3267 getAutoCreate : function()
3269 var html = this.html || '';
3271 if (this.fa !== false) {
3272 html = '<i class="fa fa-' + this.fa + '"></i>';
3277 // anchor's do not require html/href...
3278 if (this.anchor === false) {
3280 cfg.href = this.href || '#';
3282 cfg.name = this.anchor;
3283 if (this.html !== false || this.fa !== false) {
3286 if (this.href !== false) {
3287 cfg.href = this.href;
3291 if(this.alt !== false){
3296 if(this.target !== false) {
3297 cfg.target = this.target;
3303 initEvents: function() {
3305 if(!this.href || this.preventDefault){
3306 this.el.on('click', this.onClick, this);
3310 onClick : function(e)
3312 if(this.preventDefault){
3315 //Roo.log('img onclick');
3316 this.fireEvent('click', this, e);
3329 * @class Roo.bootstrap.Header
3330 * @extends Roo.bootstrap.Component
3331 * Bootstrap Header class
3332 * @cfg {String} html content of header
3333 * @cfg {Number} level (1|2|3|4|5|6) default 1
3336 * Create a new Header
3337 * @param {Object} config The config object
3341 Roo.bootstrap.Header = function(config){
3342 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3353 getAutoCreate : function(){
3358 tag: 'h' + (1 *this.level),
3359 html: this.html || ''
3371 * Ext JS Library 1.1.1
3372 * Copyright(c) 2006-2007, Ext JS, LLC.
3374 * Originally Released Under LGPL - original licence link has changed is not relivant.
3377 * <script type="text/javascript">
3381 * @class Roo.bootstrap.MenuMgr
3382 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3385 Roo.bootstrap.MenuMgr = function(){
3386 var menus, active, groups = {}, attached = false, lastShow = new Date();
3388 // private - called when first menu is created
3391 active = new Roo.util.MixedCollection();
3392 Roo.get(document).addKeyListener(27, function(){
3393 if(active.length > 0){
3401 if(active && active.length > 0){
3402 var c = active.clone();
3412 if(active.length < 1){
3413 Roo.get(document).un("mouseup", onMouseDown);
3421 var last = active.last();
3422 lastShow = new Date();
3425 Roo.get(document).on("mouseup", onMouseDown);
3430 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431 m.parentMenu.activeChild = m;
3432 }else if(last && last.isVisible()){
3433 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3438 function onBeforeHide(m){
3440 m.activeChild.hide();
3442 if(m.autoHideTimer){
3443 clearTimeout(m.autoHideTimer);
3444 delete m.autoHideTimer;
3449 function onBeforeShow(m){
3450 var pm = m.parentMenu;
3451 if(!pm && !m.allowOtherMenus){
3453 }else if(pm && pm.activeChild && active != m){
3454 pm.activeChild.hide();
3458 // private this should really trigger on mouseup..
3459 function onMouseDown(e){
3460 Roo.log("on Mouse Up");
3462 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463 Roo.log("MenuManager hideAll");
3472 function onBeforeCheck(mi, state){
3474 var g = groups[mi.group];
3475 for(var i = 0, l = g.length; i < l; i++){
3477 g[i].setChecked(false);
3486 * Hides all menus that are currently visible
3488 hideAll : function(){
3493 register : function(menu){
3497 menus[menu.id] = menu;
3498 menu.on("beforehide", onBeforeHide);
3499 menu.on("hide", onHide);
3500 menu.on("beforeshow", onBeforeShow);
3501 menu.on("show", onShow);
3503 if(g && menu.events["checkchange"]){
3507 groups[g].push(menu);
3508 menu.on("checkchange", onCheck);
3513 * Returns a {@link Roo.menu.Menu} object
3514 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515 * be used to generate and return a new Menu instance.
3517 get : function(menu){
3518 if(typeof menu == "string"){ // menu id
3520 }else if(menu.events){ // menu instance
3523 /*else if(typeof menu.length == 'number'){ // array of menu items?
3524 return new Roo.bootstrap.Menu({items:menu});
3525 }else{ // otherwise, must be a config
3526 return new Roo.bootstrap.Menu(menu);
3533 unregister : function(menu){
3534 delete menus[menu.id];
3535 menu.un("beforehide", onBeforeHide);
3536 menu.un("hide", onHide);
3537 menu.un("beforeshow", onBeforeShow);
3538 menu.un("show", onShow);
3540 if(g && menu.events["checkchange"]){
3541 groups[g].remove(menu);
3542 menu.un("checkchange", onCheck);
3547 registerCheckable : function(menuItem){
3548 var g = menuItem.group;
3553 groups[g].push(menuItem);
3554 menuItem.on("beforecheckchange", onBeforeCheck);
3559 unregisterCheckable : function(menuItem){
3560 var g = menuItem.group;
3562 groups[g].remove(menuItem);
3563 menuItem.un("beforecheckchange", onBeforeCheck);
3575 * @class Roo.bootstrap.Menu
3576 * @extends Roo.bootstrap.Component
3577 * Bootstrap Menu class - container for MenuItems
3578 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579 * @cfg {bool} hidden if the menu should be hidden when rendered.
3580 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3581 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3582 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3583 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3587 * @param {Object} config The config object
3591 Roo.bootstrap.Menu = function(config){
3593 if (config.type == 'treeview') {
3594 // normally menu's are drawn attached to the document to handle layering etc..
3595 // however treeview (used by the docs menu is drawn into the parent element)
3596 this.container_method = 'getChildContainer';
3599 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3600 if (this.registerMenu && this.type != 'treeview') {
3601 Roo.bootstrap.MenuMgr.register(this);
3608 * Fires before this menu is displayed (return false to block)
3609 * @param {Roo.menu.Menu} this
3614 * Fires before this menu is hidden (return false to block)
3615 * @param {Roo.menu.Menu} this
3620 * Fires after this menu is displayed
3621 * @param {Roo.menu.Menu} this
3626 * Fires after this menu is hidden
3627 * @param {Roo.menu.Menu} this
3632 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3633 * @param {Roo.menu.Menu} this
3634 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3635 * @param {Roo.EventObject} e
3640 * Fires when the mouse is hovering over this menu
3641 * @param {Roo.menu.Menu} this
3642 * @param {Roo.EventObject} e
3643 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3648 * Fires when the mouse exits this menu
3649 * @param {Roo.menu.Menu} this
3650 * @param {Roo.EventObject} e
3651 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3656 * Fires when a menu item contained in this menu is clicked
3657 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3658 * @param {Roo.EventObject} e
3662 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3665 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3669 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3672 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3674 registerMenu : true,
3676 menuItems :false, // stores the menu items..
3686 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3688 hideTrigger : false,
3693 getChildContainer : function() {
3697 getAutoCreate : function(){
3699 //if (['right'].indexOf(this.align)!==-1) {
3700 // cfg.cn[1].cls += ' pull-right'
3705 cls : 'dropdown-menu shadow' ,
3706 style : 'z-index:1000'
3710 if (this.type === 'submenu') {
3711 cfg.cls = 'submenu active';
3713 if (this.type === 'treeview') {
3714 cfg.cls = 'treeview-menu';
3719 initEvents : function() {
3721 // Roo.log("ADD event");
3722 // Roo.log(this.triggerEl.dom);
3723 if (this.triggerEl) {
3725 this.triggerEl.on('click', this.onTriggerClick, this);
3727 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3729 if (!this.hideTrigger) {
3730 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3731 // dropdown toggle on the 'a' in BS4?
3732 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3734 this.triggerEl.addClass('dropdown-toggle');
3740 this.el.on('touchstart' , this.onTouch, this);
3742 this.el.on('click' , this.onClick, this);
3744 this.el.on("mouseover", this.onMouseOver, this);
3745 this.el.on("mouseout", this.onMouseOut, this);
3749 findTargetItem : function(e)
3751 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3755 //Roo.log(t); Roo.log(t.id);
3757 //Roo.log(this.menuitems);
3758 return this.menuitems.get(t.id);
3760 //return this.items.get(t.menuItemId);
3766 onTouch : function(e)
3768 Roo.log("menu.onTouch");
3769 //e.stopEvent(); this make the user popdown broken
3773 onClick : function(e)
3775 Roo.log("menu.onClick");
3777 var t = this.findTargetItem(e);
3778 if(!t || t.isContainer){
3783 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3784 if(t == this.activeItem && t.shouldDeactivate(e)){
3785 this.activeItem.deactivate();
3786 delete this.activeItem;
3790 this.setActiveItem(t, true);
3798 Roo.log('pass click event');
3802 this.fireEvent("click", this, t, e);
3806 if(!t.href.length || t.href == '#'){
3807 (function() { _this.hide(); }).defer(100);
3812 onMouseOver : function(e){
3813 var t = this.findTargetItem(e);
3816 // if(t.canActivate && !t.disabled){
3817 // this.setActiveItem(t, true);
3821 this.fireEvent("mouseover", this, e, t);
3823 isVisible : function(){
3824 return !this.hidden;
3826 onMouseOut : function(e){
3827 var t = this.findTargetItem(e);
3830 // if(t == this.activeItem && t.shouldDeactivate(e)){
3831 // this.activeItem.deactivate();
3832 // delete this.activeItem;
3835 this.fireEvent("mouseout", this, e, t);
3840 * Displays this menu relative to another element
3841 * @param {String/HTMLElement/Roo.Element} element The element to align to
3842 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3843 * the element (defaults to this.defaultAlign)
3844 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3846 show : function(el, pos, parentMenu)
3848 if (false === this.fireEvent("beforeshow", this)) {
3849 Roo.log("show canceled");
3852 this.parentMenu = parentMenu;
3856 this.el.addClass('show'); // show otherwise we do not know how big we are..
3858 var xy = this.el.getAlignToXY(el, pos);
3860 // bl-tl << left align below
3861 // tl-bl << left align
3863 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3864 // if it goes to far to the right.. -> align left.
3865 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3868 // was left align - go right?
3869 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3872 // goes down the bottom
3873 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3875 var a = this.align.replace('?', '').split('-');
3876 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3880 this.showAt( xy , parentMenu, false);
3883 * Displays this menu at a specific xy position
3884 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3885 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3887 showAt : function(xy, parentMenu, /* private: */_e){
3888 this.parentMenu = parentMenu;
3893 this.fireEvent("beforeshow", this);
3894 //xy = this.el.adjustForConstraints(xy);
3898 this.hideMenuItems();
3899 this.hidden = false;
3900 if (this.triggerEl) {
3901 this.triggerEl.addClass('open');
3904 this.el.addClass('show');
3908 // reassign x when hitting right
3910 // reassign y when hitting bottom
3912 // but the list may align on trigger left or trigger top... should it be a properity?
3914 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3919 this.fireEvent("show", this);
3925 this.doFocus.defer(50, this);
3929 doFocus : function(){
3931 this.focusEl.focus();
3936 * Hides this menu and optionally all parent menus
3937 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3939 hide : function(deep)
3941 if (false === this.fireEvent("beforehide", this)) {
3942 Roo.log("hide canceled");
3945 this.hideMenuItems();
3946 if(this.el && this.isVisible()){
3948 if(this.activeItem){
3949 this.activeItem.deactivate();
3950 this.activeItem = null;
3952 if (this.triggerEl) {
3953 this.triggerEl.removeClass('open');
3956 this.el.removeClass('show');
3958 this.fireEvent("hide", this);
3960 if(deep === true && this.parentMenu){
3961 this.parentMenu.hide(true);
3965 onTriggerClick : function(e)
3967 Roo.log('trigger click');
3969 var target = e.getTarget();
3971 Roo.log(target.nodeName.toLowerCase());
3973 if(target.nodeName.toLowerCase() === 'i'){
3979 onTriggerPress : function(e)
3981 Roo.log('trigger press');
3982 //Roo.log(e.getTarget());
3983 // Roo.log(this.triggerEl.dom);
3985 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3986 var pel = Roo.get(e.getTarget());
3987 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3988 Roo.log('is treeview or dropdown?');
3992 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3996 if (this.isVisible()) {
4002 this.show(this.triggerEl, this.align, false);
4005 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4012 hideMenuItems : function()
4014 Roo.log("hide Menu Items");
4019 this.el.select('.open',true).each(function(aa) {
4021 aa.removeClass('open');
4025 addxtypeChild : function (tree, cntr) {
4026 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4028 this.menuitems.add(comp);
4040 this.getEl().dom.innerHTML = '';
4041 this.menuitems.clear();
4055 * @class Roo.bootstrap.MenuItem
4056 * @extends Roo.bootstrap.Component
4057 * Bootstrap MenuItem class
4058 * @cfg {String} html the menu label
4059 * @cfg {String} href the link
4060 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4061 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4062 * @cfg {Boolean} active used on sidebars to highlight active itesm
4063 * @cfg {String} fa favicon to show on left of menu item.
4064 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4068 * Create a new MenuItem
4069 * @param {Object} config The config object
4073 Roo.bootstrap.MenuItem = function(config){
4074 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4079 * The raw click event for the entire grid.
4080 * @param {Roo.bootstrap.MenuItem} this
4081 * @param {Roo.EventObject} e
4087 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4091 preventDefault: false,
4092 isContainer : false,
4096 getAutoCreate : function(){
4098 if(this.isContainer){
4101 cls: 'dropdown-menu-item '
4111 cls : 'dropdown-item',
4116 if (this.fa !== false) {
4119 cls : 'fa fa-' + this.fa
4128 cls: 'dropdown-menu-item',
4131 if (this.parent().type == 'treeview') {
4132 cfg.cls = 'treeview-menu';
4135 cfg.cls += ' active';
4140 anc.href = this.href || cfg.cn[0].href ;
4141 ctag.html = this.html || cfg.cn[0].html ;
4145 initEvents: function()
4147 if (this.parent().type == 'treeview') {
4148 this.el.select('a').on('click', this.onClick, this);
4152 this.menu.parentType = this.xtype;
4153 this.menu.triggerEl = this.el;
4154 this.menu = this.addxtype(Roo.apply({}, this.menu));
4158 onClick : function(e)
4160 Roo.log('item on click ');
4162 if(this.preventDefault){
4165 //this.parent().hideMenuItems();
4167 this.fireEvent('click', this, e);
4186 * @class Roo.bootstrap.MenuSeparator
4187 * @extends Roo.bootstrap.Component
4188 * Bootstrap MenuSeparator class
4191 * Create a new MenuItem
4192 * @param {Object} config The config object
4196 Roo.bootstrap.MenuSeparator = function(config){
4197 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4200 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4202 getAutoCreate : function(){
4221 * @class Roo.bootstrap.Modal
4222 * @extends Roo.bootstrap.Component
4223 * Bootstrap Modal class
4224 * @cfg {String} title Title of dialog
4225 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4226 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4227 * @cfg {Boolean} specificTitle default false
4228 * @cfg {Array} buttons Array of buttons or standard button set..
4229 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4230 * @cfg {Boolean} animate default true
4231 * @cfg {Boolean} allow_close default true
4232 * @cfg {Boolean} fitwindow default false
4233 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4234 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4235 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4236 * @cfg {String} size (sm|lg|xl) default empty
4237 * @cfg {Number} max_width set the max width of modal
4238 * @cfg {Boolean} editableTitle can the title be edited
4243 * Create a new Modal Dialog
4244 * @param {Object} config The config object
4247 Roo.bootstrap.Modal = function(config){
4248 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4253 * The raw btnclick event for the button
4254 * @param {Roo.EventObject} e
4259 * Fire when dialog resize
4260 * @param {Roo.bootstrap.Modal} this
4261 * @param {Roo.EventObject} e
4265 * @event titlechanged
4266 * Fire when the editable title has been changed
4267 * @param {Roo.bootstrap.Modal} this
4268 * @param {Roo.EventObject} value
4270 "titlechanged" : true
4273 this.buttons = this.buttons || [];
4276 this.tmpl = Roo.factory(this.tmpl);
4281 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4283 title : 'test dialog',
4293 specificTitle: false,
4295 buttonPosition: 'right',
4317 editableTitle : false,
4319 onRender : function(ct, position)
4321 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4324 var cfg = Roo.apply({}, this.getAutoCreate());
4327 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4329 //if (!cfg.name.length) {
4333 cfg.cls += ' ' + this.cls;
4336 cfg.style = this.style;
4338 this.el = Roo.get(document.body).createChild(cfg, position);
4340 //var type = this.el.dom.type;
4343 if(this.tabIndex !== undefined){
4344 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4347 this.dialogEl = this.el.select('.modal-dialog',true).first();
4348 this.bodyEl = this.el.select('.modal-body',true).first();
4349 this.closeEl = this.el.select('.modal-header .close', true).first();
4350 this.headerEl = this.el.select('.modal-header',true).first();
4351 this.titleEl = this.el.select('.modal-title',true).first();
4352 this.footerEl = this.el.select('.modal-footer',true).first();
4354 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4356 //this.el.addClass("x-dlg-modal");
4358 if (this.buttons.length) {
4359 Roo.each(this.buttons, function(bb) {
4360 var b = Roo.apply({}, bb);
4361 b.xns = b.xns || Roo.bootstrap;
4362 b.xtype = b.xtype || 'Button';
4363 if (typeof(b.listeners) == 'undefined') {
4364 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4367 var btn = Roo.factory(b);
4369 btn.render(this.getButtonContainer());
4373 // render the children.
4376 if(typeof(this.items) != 'undefined'){
4377 var items = this.items;
4380 for(var i =0;i < items.length;i++) {
4381 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4385 this.items = nitems;
4387 // where are these used - they used to be body/close/footer
4391 //this.el.addClass([this.fieldClass, this.cls]);
4395 getAutoCreate : function()
4397 // we will default to modal-body-overflow - might need to remove or make optional later.
4399 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4400 html : this.html || ''
4405 cls : 'modal-title',
4409 if(this.specificTitle){ // WTF is this?
4414 if (this.allow_close && Roo.bootstrap.version == 3) {
4424 if (this.editableTitle) {
4426 cls: 'form-control roo-editable-title d-none',
4432 if (this.allow_close && Roo.bootstrap.version == 4) {
4442 if(this.size.length){
4443 size = 'modal-' + this.size;
4446 var footer = Roo.bootstrap.version == 3 ?
4448 cls : 'modal-footer',
4452 cls: 'btn-' + this.buttonPosition
4457 { // BS4 uses mr-auto on left buttons....
4458 cls : 'modal-footer'
4469 cls: "modal-dialog " + size,
4472 cls : "modal-content",
4475 cls : 'modal-header',
4490 modal.cls += ' fade';
4496 getChildContainer : function() {
4501 getButtonContainer : function() {
4503 return Roo.bootstrap.version == 4 ?
4504 this.el.select('.modal-footer',true).first()
4505 : this.el.select('.modal-footer div',true).first();
4508 initEvents : function()
4510 if (this.allow_close) {
4511 this.closeEl.on('click', this.hide, this);
4513 Roo.EventManager.onWindowResize(this.resize, this, true);
4514 if (this.editableTitle) {
4515 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4516 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4517 this.headerEditEl.on('keyup', function(e) {
4518 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4519 this.toggleHeaderInput(false)
4522 this.headerEditEl.on('blur', function(e) {
4523 this.toggleHeaderInput(false)
4532 this.maskEl.setSize(
4533 Roo.lib.Dom.getViewWidth(true),
4534 Roo.lib.Dom.getViewHeight(true)
4537 if (this.fitwindow) {
4539 this.dialogEl.setStyle( { 'max-width' : '100%' });
4541 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4542 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4547 if(this.max_width !== 0) {
4549 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4552 this.setSize(w, this.height);
4556 if(this.max_height) {
4557 this.setSize(w,Math.min(
4559 Roo.lib.Dom.getViewportHeight(true) - 60
4565 if(!this.fit_content) {
4566 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4570 this.setSize(w, Math.min(
4572 this.headerEl.getHeight() +
4573 this.footerEl.getHeight() +
4574 this.getChildHeight(this.bodyEl.dom.childNodes),
4575 Roo.lib.Dom.getViewportHeight(true) - 60)
4581 setSize : function(w,h)
4592 if (!this.rendered) {
4595 this.toggleHeaderInput(false);
4596 //this.el.setStyle('display', 'block');
4597 this.el.removeClass('hideing');
4598 this.el.dom.style.display='block';
4600 Roo.get(document.body).addClass('modal-open');
4602 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4605 this.el.addClass('show');
4606 this.el.addClass('in');
4609 this.el.addClass('show');
4610 this.el.addClass('in');
4613 // not sure how we can show data in here..
4615 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4618 Roo.get(document.body).addClass("x-body-masked");
4620 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4621 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4622 this.maskEl.dom.style.display = 'block';
4623 this.maskEl.addClass('show');
4628 this.fireEvent('show', this);
4630 // set zindex here - otherwise it appears to be ignored...
4631 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4634 this.items.forEach( function(e) {
4635 e.layout ? e.layout() : false;
4643 if(this.fireEvent("beforehide", this) !== false){
4645 this.maskEl.removeClass('show');
4647 this.maskEl.dom.style.display = '';
4648 Roo.get(document.body).removeClass("x-body-masked");
4649 this.el.removeClass('in');
4650 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4652 if(this.animate){ // why
4653 this.el.addClass('hideing');
4654 this.el.removeClass('show');
4656 if (!this.el.hasClass('hideing')) {
4657 return; // it's been shown again...
4660 this.el.dom.style.display='';
4662 Roo.get(document.body).removeClass('modal-open');
4663 this.el.removeClass('hideing');
4667 this.el.removeClass('show');
4668 this.el.dom.style.display='';
4669 Roo.get(document.body).removeClass('modal-open');
4672 this.fireEvent('hide', this);
4675 isVisible : function()
4678 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4682 addButton : function(str, cb)
4686 var b = Roo.apply({}, { html : str } );
4687 b.xns = b.xns || Roo.bootstrap;
4688 b.xtype = b.xtype || 'Button';
4689 if (typeof(b.listeners) == 'undefined') {
4690 b.listeners = { click : cb.createDelegate(this) };
4693 var btn = Roo.factory(b);
4695 btn.render(this.getButtonContainer());
4701 setDefaultButton : function(btn)
4703 //this.el.select('.modal-footer').()
4706 resizeTo: function(w,h)
4708 this.dialogEl.setWidth(w);
4710 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4712 this.bodyEl.setHeight(h - diff);
4714 this.fireEvent('resize', this);
4717 setContentSize : function(w, h)
4721 onButtonClick: function(btn,e)
4724 this.fireEvent('btnclick', btn.name, e);
4727 * Set the title of the Dialog
4728 * @param {String} str new Title
4730 setTitle: function(str) {
4731 this.titleEl.dom.innerHTML = str;
4735 * Set the body of the Dialog
4736 * @param {String} str new Title
4738 setBody: function(str) {
4739 this.bodyEl.dom.innerHTML = str;
4742 * Set the body of the Dialog using the template
4743 * @param {Obj} data - apply this data to the template and replace the body contents.
4745 applyBody: function(obj)
4748 Roo.log("Error - using apply Body without a template");
4751 this.tmpl.overwrite(this.bodyEl, obj);
4754 getChildHeight : function(child_nodes)
4758 child_nodes.length == 0
4763 var child_height = 0;
4765 for(var i = 0; i < child_nodes.length; i++) {
4768 * for modal with tabs...
4769 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4771 var layout_childs = child_nodes[i].childNodes;
4773 for(var j = 0; j < layout_childs.length; j++) {
4775 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4777 var layout_body_childs = layout_childs[j].childNodes;
4779 for(var k = 0; k < layout_body_childs.length; k++) {
4781 if(layout_body_childs[k].classList.contains('navbar')) {
4782 child_height += layout_body_childs[k].offsetHeight;
4786 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4788 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4790 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4792 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4793 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4808 child_height += child_nodes[i].offsetHeight;
4809 // Roo.log(child_nodes[i].offsetHeight);
4812 return child_height;
4814 toggleHeaderInput : function(is_edit)
4816 if (!this.editableTitle) {
4817 return; // not editable.
4819 if (is_edit && this.is_header_editing) {
4820 return; // already editing..
4824 this.headerEditEl.dom.value = this.title;
4825 this.headerEditEl.removeClass('d-none');
4826 this.headerEditEl.dom.focus();
4827 this.titleEl.addClass('d-none');
4829 this.is_header_editing = true;
4832 // flip back to not editing.
4833 this.title = this.headerEditEl.dom.value;
4834 this.headerEditEl.addClass('d-none');
4835 this.titleEl.removeClass('d-none');
4836 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4837 this.is_header_editing = false;
4838 this.fireEvent('titlechanged', this, this.title);
4847 Roo.apply(Roo.bootstrap.Modal, {
4849 * Button config that displays a single OK button
4858 * Button config that displays Yes and No buttons
4874 * Button config that displays OK and Cancel buttons
4889 * Button config that displays Yes, No and Cancel buttons
4914 * messagebox - can be used as a replace
4918 * @class Roo.MessageBox
4919 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4923 Roo.Msg.alert('Status', 'Changes saved successfully.');
4925 // Prompt for user data:
4926 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4928 // process text value...
4932 // Show a dialog using config options:
4934 title:'Save Changes?',
4935 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4936 buttons: Roo.Msg.YESNOCANCEL,
4943 Roo.bootstrap.MessageBox = function(){
4944 var dlg, opt, mask, waitTimer;
4945 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4946 var buttons, activeTextEl, bwidth;
4950 var handleButton = function(button){
4952 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4956 var handleHide = function(){
4958 dlg.el.removeClass(opt.cls);
4961 // Roo.TaskMgr.stop(waitTimer);
4962 // waitTimer = null;
4967 var updateButtons = function(b){
4970 buttons["ok"].hide();
4971 buttons["cancel"].hide();
4972 buttons["yes"].hide();
4973 buttons["no"].hide();
4974 dlg.footerEl.hide();
4978 dlg.footerEl.show();
4979 for(var k in buttons){
4980 if(typeof buttons[k] != "function"){
4983 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4984 width += buttons[k].el.getWidth()+15;
4994 var handleEsc = function(d, k, e){
4995 if(opt && opt.closable !== false){
5005 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5006 * @return {Roo.BasicDialog} The BasicDialog element
5008 getDialog : function(){
5010 dlg = new Roo.bootstrap.Modal( {
5013 //constraintoviewport:false,
5015 //collapsible : false,
5020 //buttonAlign:"center",
5021 closeClick : function(){
5022 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5025 handleButton("cancel");
5030 dlg.on("hide", handleHide);
5032 //dlg.addKeyListener(27, handleEsc);
5034 this.buttons = buttons;
5035 var bt = this.buttonText;
5036 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5037 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5038 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5039 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5041 bodyEl = dlg.bodyEl.createChild({
5043 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5044 '<textarea class="roo-mb-textarea"></textarea>' +
5045 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5047 msgEl = bodyEl.dom.firstChild;
5048 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5049 textboxEl.enableDisplayMode();
5050 textboxEl.addKeyListener([10,13], function(){
5051 if(dlg.isVisible() && opt && opt.buttons){
5054 }else if(opt.buttons.yes){
5055 handleButton("yes");
5059 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5060 textareaEl.enableDisplayMode();
5061 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5062 progressEl.enableDisplayMode();
5064 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5065 var pf = progressEl.dom.firstChild;
5067 pp = Roo.get(pf.firstChild);
5068 pp.setHeight(pf.offsetHeight);
5076 * Updates the message box body text
5077 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5078 * the XHTML-compliant non-breaking space character '&#160;')
5079 * @return {Roo.MessageBox} This message box
5081 updateText : function(text)
5083 if(!dlg.isVisible() && !opt.width){
5084 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5085 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5087 msgEl.innerHTML = text || ' ';
5089 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5090 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5092 Math.min(opt.width || cw , this.maxWidth),
5093 Math.max(opt.minWidth || this.minWidth, bwidth)
5096 activeTextEl.setWidth(w);
5098 if(dlg.isVisible()){
5099 dlg.fixedcenter = false;
5101 // to big, make it scroll. = But as usual stupid IE does not support
5104 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5105 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5106 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5108 bodyEl.dom.style.height = '';
5109 bodyEl.dom.style.overflowY = '';
5112 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5114 bodyEl.dom.style.overflowX = '';
5117 dlg.setContentSize(w, bodyEl.getHeight());
5118 if(dlg.isVisible()){
5119 dlg.fixedcenter = true;
5125 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5126 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5127 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5128 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5129 * @return {Roo.MessageBox} This message box
5131 updateProgress : function(value, text){
5133 this.updateText(text);
5136 if (pp) { // weird bug on my firefox - for some reason this is not defined
5137 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5138 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5144 * Returns true if the message box is currently displayed
5145 * @return {Boolean} True if the message box is visible, else false
5147 isVisible : function(){
5148 return dlg && dlg.isVisible();
5152 * Hides the message box if it is displayed
5155 if(this.isVisible()){
5161 * Displays a new message box, or reinitializes an existing message box, based on the config options
5162 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5163 * The following config object properties are supported:
5165 Property Type Description
5166 ---------- --------------- ------------------------------------------------------------------------------------
5167 animEl String/Element An id or Element from which the message box should animate as it opens and
5168 closes (defaults to undefined)
5169 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5170 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5171 closable Boolean False to hide the top-right close button (defaults to true). Note that
5172 progress and wait dialogs will ignore this property and always hide the
5173 close button as they can only be closed programmatically.
5174 cls String A custom CSS class to apply to the message box element
5175 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5176 displayed (defaults to 75)
5177 fn Function A callback function to execute after closing the dialog. The arguments to the
5178 function will be btn (the name of the button that was clicked, if applicable,
5179 e.g. "ok"), and text (the value of the active text field, if applicable).
5180 Progress and wait dialogs will ignore this option since they do not respond to
5181 user actions and can only be closed programmatically, so any required function
5182 should be called by the same code after it closes the dialog.
5183 icon String A CSS class that provides a background image to be used as an icon for
5184 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5185 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5186 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5187 modal Boolean False to allow user interaction with the page while the message box is
5188 displayed (defaults to true)
5189 msg String A string that will replace the existing message box body text (defaults
5190 to the XHTML-compliant non-breaking space character ' ')
5191 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5192 progress Boolean True to display a progress bar (defaults to false)
5193 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5194 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5195 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5196 title String The title text
5197 value String The string value to set into the active textbox element if displayed
5198 wait Boolean True to display a progress bar (defaults to false)
5199 width Number The width of the dialog in pixels
5206 msg: 'Please enter your address:',
5208 buttons: Roo.MessageBox.OKCANCEL,
5211 animEl: 'addAddressBtn'
5214 * @param {Object} config Configuration options
5215 * @return {Roo.MessageBox} This message box
5217 show : function(options)
5220 // this causes nightmares if you show one dialog after another
5221 // especially on callbacks..
5223 if(this.isVisible()){
5226 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5227 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5228 Roo.log("New Dialog Message:" + options.msg )
5229 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5230 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5233 var d = this.getDialog();
5235 d.setTitle(opt.title || " ");
5236 d.closeEl.setDisplayed(opt.closable !== false);
5237 activeTextEl = textboxEl;
5238 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5243 textareaEl.setHeight(typeof opt.multiline == "number" ?
5244 opt.multiline : this.defaultTextHeight);
5245 activeTextEl = textareaEl;
5254 progressEl.setDisplayed(opt.progress === true);
5256 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5258 this.updateProgress(0);
5259 activeTextEl.dom.value = opt.value || "";
5261 dlg.setDefaultButton(activeTextEl);
5263 var bs = opt.buttons;
5267 }else if(bs && bs.yes){
5268 db = buttons["yes"];
5270 dlg.setDefaultButton(db);
5272 bwidth = updateButtons(opt.buttons);
5273 this.updateText(opt.msg);
5275 d.el.addClass(opt.cls);
5277 d.proxyDrag = opt.proxyDrag === true;
5278 d.modal = opt.modal !== false;
5279 d.mask = opt.modal !== false ? mask : false;
5281 // force it to the end of the z-index stack so it gets a cursor in FF
5282 document.body.appendChild(dlg.el.dom);
5283 d.animateTarget = null;
5284 d.show(options.animEl);
5290 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5291 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5292 * and closing the message box when the process is complete.
5293 * @param {String} title The title bar text
5294 * @param {String} msg The message box body text
5295 * @return {Roo.MessageBox} This message box
5297 progress : function(title, msg){
5304 minWidth: this.minProgressWidth,
5311 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5312 * If a callback function is passed it will be called after the user clicks the button, and the
5313 * id of the button that was clicked will be passed as the only parameter to the callback
5314 * (could also be the top-right close button).
5315 * @param {String} title The title bar text
5316 * @param {String} msg The message box body text
5317 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5318 * @param {Object} scope (optional) The scope of the callback function
5319 * @return {Roo.MessageBox} This message box
5321 alert : function(title, msg, fn, scope)
5336 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5337 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5338 * You are responsible for closing the message box when the process is complete.
5339 * @param {String} msg The message box body text
5340 * @param {String} title (optional) The title bar text
5341 * @return {Roo.MessageBox} This message box
5343 wait : function(msg, title){
5354 waitTimer = Roo.TaskMgr.start({
5356 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5364 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5365 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5366 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5367 * @param {String} title The title bar text
5368 * @param {String} msg The message box body text
5369 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5370 * @param {Object} scope (optional) The scope of the callback function
5371 * @return {Roo.MessageBox} This message box
5373 confirm : function(title, msg, fn, scope){
5377 buttons: this.YESNO,
5386 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5387 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5388 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5389 * (could also be the top-right close button) and the text that was entered will be passed as the two
5390 * parameters to the callback.
5391 * @param {String} title The title bar text
5392 * @param {String} msg The message box body text
5393 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5394 * @param {Object} scope (optional) The scope of the callback function
5395 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5396 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5397 * @return {Roo.MessageBox} This message box
5399 prompt : function(title, msg, fn, scope, multiline){
5403 buttons: this.OKCANCEL,
5408 multiline: multiline,
5415 * Button config that displays a single OK button
5420 * Button config that displays Yes and No buttons
5423 YESNO : {yes:true, no:true},
5425 * Button config that displays OK and Cancel buttons
5428 OKCANCEL : {ok:true, cancel:true},
5430 * Button config that displays Yes, No and Cancel buttons
5433 YESNOCANCEL : {yes:true, no:true, cancel:true},
5436 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5439 defaultTextHeight : 75,
5441 * The maximum width in pixels of the message box (defaults to 600)
5446 * The minimum width in pixels of the message box (defaults to 100)
5451 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5452 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5455 minProgressWidth : 250,
5457 * An object containing the default button text strings that can be overriden for localized language support.
5458 * Supported properties are: ok, cancel, yes and no.
5459 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5472 * Shorthand for {@link Roo.MessageBox}
5474 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5475 Roo.Msg = Roo.Msg || Roo.MessageBox;
5484 * @class Roo.bootstrap.Navbar
5485 * @extends Roo.bootstrap.Component
5486 * Bootstrap Navbar class
5489 * Create a new Navbar
5490 * @param {Object} config The config object
5494 Roo.bootstrap.Navbar = function(config){
5495 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5499 * @event beforetoggle
5500 * Fire before toggle the menu
5501 * @param {Roo.EventObject} e
5503 "beforetoggle" : true
5507 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5516 getAutoCreate : function(){
5519 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5523 initEvents :function ()
5525 //Roo.log(this.el.select('.navbar-toggle',true));
5526 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5533 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5535 var size = this.el.getSize();
5536 this.maskEl.setSize(size.width, size.height);
5537 this.maskEl.enableDisplayMode("block");
5546 getChildContainer : function()
5548 if (this.el && this.el.select('.collapse').getCount()) {
5549 return this.el.select('.collapse',true).first();
5564 onToggle : function()
5567 if(this.fireEvent('beforetoggle', this) === false){
5570 var ce = this.el.select('.navbar-collapse',true).first();
5572 if (!ce.hasClass('show')) {
5582 * Expand the navbar pulldown
5584 expand : function ()
5587 var ce = this.el.select('.navbar-collapse',true).first();
5588 if (ce.hasClass('collapsing')) {
5591 ce.dom.style.height = '';
5593 ce.addClass('in'); // old...
5594 ce.removeClass('collapse');
5595 ce.addClass('show');
5596 var h = ce.getHeight();
5598 ce.removeClass('show');
5599 // at this point we should be able to see it..
5600 ce.addClass('collapsing');
5602 ce.setHeight(0); // resize it ...
5603 ce.on('transitionend', function() {
5604 //Roo.log('done transition');
5605 ce.removeClass('collapsing');
5606 ce.addClass('show');
5607 ce.removeClass('collapse');
5609 ce.dom.style.height = '';
5610 }, this, { single: true} );
5612 ce.dom.scrollTop = 0;
5615 * Collapse the navbar pulldown
5617 collapse : function()
5619 var ce = this.el.select('.navbar-collapse',true).first();
5621 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5622 // it's collapsed or collapsing..
5625 ce.removeClass('in'); // old...
5626 ce.setHeight(ce.getHeight());
5627 ce.removeClass('show');
5628 ce.addClass('collapsing');
5630 ce.on('transitionend', function() {
5631 ce.dom.style.height = '';
5632 ce.removeClass('collapsing');
5633 ce.addClass('collapse');
5634 }, this, { single: true} );
5654 * @class Roo.bootstrap.NavSimplebar
5655 * @extends Roo.bootstrap.Navbar
5656 * Bootstrap Sidebar class
5658 * @cfg {Boolean} inverse is inverted color
5660 * @cfg {String} type (nav | pills | tabs)
5661 * @cfg {Boolean} arrangement stacked | justified
5662 * @cfg {String} align (left | right) alignment
5664 * @cfg {Boolean} main (true|false) main nav bar? default false
5665 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5667 * @cfg {String} tag (header|footer|nav|div) default is nav
5669 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5673 * Create a new Sidebar
5674 * @param {Object} config The config object
5678 Roo.bootstrap.NavSimplebar = function(config){
5679 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5682 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5698 getAutoCreate : function(){
5702 tag : this.tag || 'div',
5703 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5705 if (['light','white'].indexOf(this.weight) > -1) {
5706 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5708 cfg.cls += ' bg-' + this.weight;
5711 cfg.cls += ' navbar-inverse';
5715 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5717 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5726 cls: 'nav nav-' + this.xtype,
5732 this.type = this.type || 'nav';
5733 if (['tabs','pills'].indexOf(this.type) != -1) {
5734 cfg.cn[0].cls += ' nav-' + this.type
5738 if (this.type!=='nav') {
5739 Roo.log('nav type must be nav/tabs/pills')
5741 cfg.cn[0].cls += ' navbar-nav'
5747 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5748 cfg.cn[0].cls += ' nav-' + this.arrangement;
5752 if (this.align === 'right') {
5753 cfg.cn[0].cls += ' navbar-right';
5778 * navbar-expand-md fixed-top
5782 * @class Roo.bootstrap.NavHeaderbar
5783 * @extends Roo.bootstrap.NavSimplebar
5784 * Bootstrap Sidebar class
5786 * @cfg {String} brand what is brand
5787 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5788 * @cfg {String} brand_href href of the brand
5789 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5790 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5791 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5792 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5795 * Create a new Sidebar
5796 * @param {Object} config The config object
5800 Roo.bootstrap.NavHeaderbar = function(config){
5801 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5805 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5812 desktopCenter : false,
5815 getAutoCreate : function(){
5818 tag: this.nav || 'nav',
5819 cls: 'navbar navbar-expand-md',
5825 if (this.desktopCenter) {
5826 cn.push({cls : 'container', cn : []});
5834 cls: 'navbar-toggle navbar-toggler',
5835 'data-toggle': 'collapse',
5840 html: 'Toggle navigation'
5844 cls: 'icon-bar navbar-toggler-icon'
5857 cn.push( Roo.bootstrap.version == 4 ? btn : {
5859 cls: 'navbar-header',
5868 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5872 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5874 if (['light','white'].indexOf(this.weight) > -1) {
5875 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5877 cfg.cls += ' bg-' + this.weight;
5880 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5881 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5883 // tag can override this..
5885 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5888 if (this.brand !== '') {
5889 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5890 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5892 href: this.brand_href ? this.brand_href : '#',
5893 cls: 'navbar-brand',
5901 cfg.cls += ' main-nav';
5909 getHeaderChildContainer : function()
5911 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5912 return this.el.select('.navbar-header',true).first();
5915 return this.getChildContainer();
5918 getChildContainer : function()
5921 return this.el.select('.roo-navbar-collapse',true).first();
5926 initEvents : function()
5928 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5930 if (this.autohide) {
5935 Roo.get(document).on('scroll',function(e) {
5936 var ns = Roo.get(document).getScroll().top;
5937 var os = prevScroll;
5941 ft.removeClass('slideDown');
5942 ft.addClass('slideUp');
5945 ft.removeClass('slideUp');
5946 ft.addClass('slideDown');
5967 * @class Roo.bootstrap.NavSidebar
5968 * @extends Roo.bootstrap.Navbar
5969 * Bootstrap Sidebar class
5972 * Create a new Sidebar
5973 * @param {Object} config The config object
5977 Roo.bootstrap.NavSidebar = function(config){
5978 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5981 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5983 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5985 getAutoCreate : function(){
5990 cls: 'sidebar sidebar-nav'
6012 * @class Roo.bootstrap.NavGroup
6013 * @extends Roo.bootstrap.Component
6014 * Bootstrap NavGroup class
6015 * @cfg {String} align (left|right)
6016 * @cfg {Boolean} inverse
6017 * @cfg {String} type (nav|pills|tab) default nav
6018 * @cfg {String} navId - reference Id for navbar.
6019 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6022 * Create a new nav group
6023 * @param {Object} config The config object
6026 Roo.bootstrap.NavGroup = function(config){
6027 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6030 Roo.bootstrap.NavGroup.register(this);
6034 * Fires when the active item changes
6035 * @param {Roo.bootstrap.NavGroup} this
6036 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6037 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6044 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6056 getAutoCreate : function()
6058 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6064 if (Roo.bootstrap.version == 4) {
6065 if (['tabs','pills'].indexOf(this.type) != -1) {
6066 cfg.cls += ' nav-' + this.type;
6068 // trying to remove so header bar can right align top?
6069 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6070 // do not use on header bar...
6071 cfg.cls += ' navbar-nav';
6076 if (['tabs','pills'].indexOf(this.type) != -1) {
6077 cfg.cls += ' nav-' + this.type
6079 if (this.type !== 'nav') {
6080 Roo.log('nav type must be nav/tabs/pills')
6082 cfg.cls += ' navbar-nav'
6086 if (this.parent() && this.parent().sidebar) {
6089 cls: 'dashboard-menu sidebar-menu'
6095 if (this.form === true) {
6098 cls: 'navbar-form form-inline'
6100 //nav navbar-right ml-md-auto
6101 if (this.align === 'right') {
6102 cfg.cls += ' navbar-right ml-md-auto';
6104 cfg.cls += ' navbar-left';
6108 if (this.align === 'right') {
6109 cfg.cls += ' navbar-right ml-md-auto';
6111 cfg.cls += ' mr-auto';
6115 cfg.cls += ' navbar-inverse';
6123 * sets the active Navigation item
6124 * @param {Roo.bootstrap.NavItem} the new current navitem
6126 setActiveItem : function(item)
6129 Roo.each(this.navItems, function(v){
6134 v.setActive(false, true);
6141 item.setActive(true, true);
6142 this.fireEvent('changed', this, item, prev);
6147 * gets the active Navigation item
6148 * @return {Roo.bootstrap.NavItem} the current navitem
6150 getActive : function()
6154 Roo.each(this.navItems, function(v){
6165 indexOfNav : function()
6169 Roo.each(this.navItems, function(v,i){
6180 * adds a Navigation item
6181 * @param {Roo.bootstrap.NavItem} the navitem to add
6183 addItem : function(cfg)
6185 if (this.form && Roo.bootstrap.version == 4) {
6188 var cn = new Roo.bootstrap.NavItem(cfg);
6190 cn.parentId = this.id;
6191 cn.onRender(this.el, null);
6195 * register a Navigation item
6196 * @param {Roo.bootstrap.NavItem} the navitem to add
6198 register : function(item)
6200 this.navItems.push( item);
6201 item.navId = this.navId;
6206 * clear all the Navigation item
6209 clearAll : function()
6212 this.el.dom.innerHTML = '';
6215 getNavItem: function(tabId)
6218 Roo.each(this.navItems, function(e) {
6219 if (e.tabId == tabId) {
6229 setActiveNext : function()
6231 var i = this.indexOfNav(this.getActive());
6232 if (i > this.navItems.length) {
6235 this.setActiveItem(this.navItems[i+1]);
6237 setActivePrev : function()
6239 var i = this.indexOfNav(this.getActive());
6243 this.setActiveItem(this.navItems[i-1]);
6245 clearWasActive : function(except) {
6246 Roo.each(this.navItems, function(e) {
6247 if (e.tabId != except.tabId && e.was_active) {
6248 e.was_active = false;
6255 getWasActive : function ()
6258 Roo.each(this.navItems, function(e) {
6273 Roo.apply(Roo.bootstrap.NavGroup, {
6277 * register a Navigation Group
6278 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6280 register : function(navgrp)
6282 this.groups[navgrp.navId] = navgrp;
6286 * fetch a Navigation Group based on the navigation ID
6287 * @param {string} the navgroup to add
6288 * @returns {Roo.bootstrap.NavGroup} the navgroup
6290 get: function(navId) {
6291 if (typeof(this.groups[navId]) == 'undefined') {
6293 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6295 return this.groups[navId] ;
6310 * @class Roo.bootstrap.NavItem
6311 * @extends Roo.bootstrap.Component
6312 * Bootstrap Navbar.NavItem class
6313 * @cfg {String} href link to
6314 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6315 * @cfg {Boolean} button_outline show and outlined button
6316 * @cfg {String} html content of button
6317 * @cfg {String} badge text inside badge
6318 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6319 * @cfg {String} glyphicon DEPRICATED - use fa
6320 * @cfg {String} icon DEPRICATED - use fa
6321 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6322 * @cfg {Boolean} active Is item active
6323 * @cfg {Boolean} disabled Is item disabled
6324 * @cfg {String} linkcls Link Class
6325 * @cfg {Boolean} preventDefault (true | false) default false
6326 * @cfg {String} tabId the tab that this item activates.
6327 * @cfg {String} tagtype (a|span) render as a href or span?
6328 * @cfg {Boolean} animateRef (true|false) link to element default false
6331 * Create a new Navbar Item
6332 * @param {Object} config The config object
6334 Roo.bootstrap.NavItem = function(config){
6335 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6340 * The raw click event for the entire grid.
6341 * @param {Roo.EventObject} e
6346 * Fires when the active item active state changes
6347 * @param {Roo.bootstrap.NavItem} this
6348 * @param {boolean} state the new state
6354 * Fires when scroll to element
6355 * @param {Roo.bootstrap.NavItem} this
6356 * @param {Object} options
6357 * @param {Roo.EventObject} e
6365 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6374 preventDefault : false,
6382 button_outline : false,
6386 getAutoCreate : function(){
6393 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6396 cfg.cls += ' active' ;
6398 if (this.disabled) {
6399 cfg.cls += ' disabled';
6403 if (this.button_weight.length) {
6404 cfg.tag = this.href ? 'a' : 'button';
6405 cfg.html = this.html || '';
6406 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6408 cfg.href = this.href;
6411 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6413 cfg.cls += " nav-html";
6416 // menu .. should add dropdown-menu class - so no need for carat..
6418 if (this.badge !== '') {
6420 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6425 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6429 href : this.href || "#",
6430 html: this.html || '',
6434 if (this.tagtype == 'a') {
6435 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6439 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6440 } else if (this.fa) {
6441 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6442 } else if(this.glyphicon) {
6443 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6445 cfg.cn[0].cls += " nav-html";
6449 cfg.cn[0].html += " <span class='caret'></span>";
6453 if (this.badge !== '') {
6454 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6462 onRender : function(ct, position)
6464 // Roo.log("Call onRender: " + this.xtype);
6465 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6469 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6470 this.navLink = this.el.select('.nav-link',true).first();
6471 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6476 initEvents: function()
6478 if (typeof (this.menu) != 'undefined') {
6479 this.menu.parentType = this.xtype;
6480 this.menu.triggerEl = this.el;
6481 this.menu = this.addxtype(Roo.apply({}, this.menu));
6484 this.el.on('click', this.onClick, this);
6486 //if(this.tagtype == 'span'){
6487 // this.el.select('span',true).on('click', this.onClick, this);
6490 // at this point parent should be available..
6491 this.parent().register(this);
6494 onClick : function(e)
6496 if (e.getTarget('.dropdown-menu-item')) {
6497 // did you click on a menu itemm.... - then don't trigger onclick..
6502 this.preventDefault ||
6505 Roo.log("NavItem - prevent Default?");
6509 if (this.disabled) {
6513 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6514 if (tg && tg.transition) {
6515 Roo.log("waiting for the transitionend");
6521 //Roo.log("fire event clicked");
6522 if(this.fireEvent('click', this, e) === false){
6526 if(this.tagtype == 'span'){
6530 //Roo.log(this.href);
6531 var ael = this.el.select('a',true).first();
6534 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6535 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6536 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6537 return; // ignore... - it's a 'hash' to another page.
6539 Roo.log("NavItem - prevent Default?");
6541 this.scrollToElement(e);
6545 var p = this.parent();
6547 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6548 if (typeof(p.setActiveItem) !== 'undefined') {
6549 p.setActiveItem(this);
6553 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6554 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6555 // remove the collapsed menu expand...
6556 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6560 isActive: function () {
6563 setActive : function(state, fire, is_was_active)
6565 if (this.active && !state && this.navId) {
6566 this.was_active = true;
6567 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6569 nv.clearWasActive(this);
6573 this.active = state;
6576 this.el.removeClass('active');
6577 this.navLink ? this.navLink.removeClass('active') : false;
6578 } else if (!this.el.hasClass('active')) {
6580 this.el.addClass('active');
6581 if (Roo.bootstrap.version == 4 && this.navLink ) {
6582 this.navLink.addClass('active');
6587 this.fireEvent('changed', this, state);
6590 // show a panel if it's registered and related..
6592 if (!this.navId || !this.tabId || !state || is_was_active) {
6596 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6600 var pan = tg.getPanelByName(this.tabId);
6604 // if we can not flip to new panel - go back to old nav highlight..
6605 if (false == tg.showPanel(pan)) {
6606 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6608 var onav = nv.getWasActive();
6610 onav.setActive(true, false, true);
6619 // this should not be here...
6620 setDisabled : function(state)
6622 this.disabled = state;
6624 this.el.removeClass('disabled');
6625 } else if (!this.el.hasClass('disabled')) {
6626 this.el.addClass('disabled');
6632 * Fetch the element to display the tooltip on.
6633 * @return {Roo.Element} defaults to this.el
6635 tooltipEl : function()
6637 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6640 scrollToElement : function(e)
6642 var c = document.body;
6645 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6647 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6648 c = document.documentElement;
6651 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6657 var o = target.calcOffsetsTo(c);
6664 this.fireEvent('scrollto', this, options, e);
6666 Roo.get(c).scrollTo('top', options.value, true);
6671 * Set the HTML (text content) of the item
6672 * @param {string} html content for the nav item
6674 setHtml : function(html)
6677 this.htmlEl.dom.innerHTML = html;
6689 * <span> icon </span>
6690 * <span> text </span>
6691 * <span>badge </span>
6695 * @class Roo.bootstrap.NavSidebarItem
6696 * @extends Roo.bootstrap.NavItem
6697 * Bootstrap Navbar.NavSidebarItem class
6698 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6699 * {Boolean} open is the menu open
6700 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6701 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6702 * {String} buttonSize (sm|md|lg)the extra classes for the button
6703 * {Boolean} showArrow show arrow next to the text (default true)
6705 * Create a new Navbar Button
6706 * @param {Object} config The config object
6708 Roo.bootstrap.NavSidebarItem = function(config){
6709 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6714 * The raw click event for the entire grid.
6715 * @param {Roo.EventObject} e
6720 * Fires when the active item active state changes
6721 * @param {Roo.bootstrap.NavSidebarItem} this
6722 * @param {boolean} state the new state
6730 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6732 badgeWeight : 'default',
6738 buttonWeight : 'default',
6744 getAutoCreate : function(){
6749 href : this.href || '#',
6755 if(this.buttonView){
6758 href : this.href || '#',
6759 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6772 cfg.cls += ' active';
6775 if (this.disabled) {
6776 cfg.cls += ' disabled';
6779 cfg.cls += ' open x-open';
6782 if (this.glyphicon || this.icon) {
6783 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6784 a.cn.push({ tag : 'i', cls : c }) ;
6787 if(!this.buttonView){
6790 html : this.html || ''
6797 if (this.badge !== '') {
6798 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6804 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6807 a.cls += ' dropdown-toggle treeview' ;
6813 initEvents : function()
6815 if (typeof (this.menu) != 'undefined') {
6816 this.menu.parentType = this.xtype;
6817 this.menu.triggerEl = this.el;
6818 this.menu = this.addxtype(Roo.apply({}, this.menu));
6821 this.el.on('click', this.onClick, this);
6823 if(this.badge !== ''){
6824 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6829 onClick : function(e)
6836 if(this.preventDefault){
6840 this.fireEvent('click', this, e);
6843 disable : function()
6845 this.setDisabled(true);
6850 this.setDisabled(false);
6853 setDisabled : function(state)
6855 if(this.disabled == state){
6859 this.disabled = state;
6862 this.el.addClass('disabled');
6866 this.el.removeClass('disabled');
6871 setActive : function(state)
6873 if(this.active == state){
6877 this.active = state;
6880 this.el.addClass('active');
6884 this.el.removeClass('active');
6889 isActive: function ()
6894 setBadge : function(str)
6900 this.badgeEl.dom.innerHTML = str;
6915 Roo.namespace('Roo.bootstrap.breadcrumb');
6919 * @class Roo.bootstrap.breadcrumb.Nav
6920 * @extends Roo.bootstrap.Component
6921 * Bootstrap Breadcrumb Nav Class
6923 * @children Roo.bootstrap.breadcrumb.Item
6926 * Create a new breadcrumb.Nav
6927 * @param {Object} config The config object
6931 Roo.bootstrap.breadcrumb.Nav = function(config){
6932 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6937 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6939 getAutoCreate : function()
6956 initEvents: function()
6958 this.olEl = this.el.select('ol',true).first();
6960 getChildContainer : function()
6976 * @class Roo.bootstrap.breadcrumb.Nav
6977 * @extends Roo.bootstrap.Component
6978 * Bootstrap Breadcrumb Nav Class
6980 * @children Roo.bootstrap.breadcrumb.Component
6981 * @cfg {String} html the content of the link.
6982 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6983 * @cfg {Boolean} active is it active
6987 * Create a new breadcrumb.Nav
6988 * @param {Object} config The config object
6991 Roo.bootstrap.breadcrumb.Item = function(config){
6992 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6997 * The img click event for the img.
6998 * @param {Roo.EventObject} e
7005 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7010 getAutoCreate : function()
7015 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7017 if (this.href !== false) {
7024 cfg.html = this.html;
7030 initEvents: function()
7033 this.el.select('a', true).first().on('click',this.onClick, this)
7037 onClick : function(e)
7040 this.fireEvent('click',this, e);
7053 * @class Roo.bootstrap.Row
7054 * @extends Roo.bootstrap.Component
7055 * Bootstrap Row class (contains columns...)
7059 * @param {Object} config The config object
7062 Roo.bootstrap.Row = function(config){
7063 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7066 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7068 getAutoCreate : function(){
7087 * @class Roo.bootstrap.Pagination
7088 * @extends Roo.bootstrap.Component
7089 * Bootstrap Pagination class
7090 * @cfg {String} size xs | sm | md | lg
7091 * @cfg {Boolean} inverse false | true
7094 * Create a new Pagination
7095 * @param {Object} config The config object
7098 Roo.bootstrap.Pagination = function(config){
7099 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7102 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7108 getAutoCreate : function(){
7114 cfg.cls += ' inverse';
7120 cfg.cls += " " + this.cls;
7138 * @class Roo.bootstrap.PaginationItem
7139 * @extends Roo.bootstrap.Component
7140 * Bootstrap PaginationItem class
7141 * @cfg {String} html text
7142 * @cfg {String} href the link
7143 * @cfg {Boolean} preventDefault (true | false) default true
7144 * @cfg {Boolean} active (true | false) default false
7145 * @cfg {Boolean} disabled default false
7149 * Create a new PaginationItem
7150 * @param {Object} config The config object
7154 Roo.bootstrap.PaginationItem = function(config){
7155 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7160 * The raw click event for the entire grid.
7161 * @param {Roo.EventObject} e
7167 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7171 preventDefault: true,
7176 getAutoCreate : function(){
7182 href : this.href ? this.href : '#',
7183 html : this.html ? this.html : ''
7193 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7197 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7203 initEvents: function() {
7205 this.el.on('click', this.onClick, this);
7208 onClick : function(e)
7210 Roo.log('PaginationItem on click ');
7211 if(this.preventDefault){
7219 this.fireEvent('click', this, e);
7235 * @class Roo.bootstrap.Slider
7236 * @extends Roo.bootstrap.Component
7237 * Bootstrap Slider class
7240 * Create a new Slider
7241 * @param {Object} config The config object
7244 Roo.bootstrap.Slider = function(config){
7245 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7248 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7250 getAutoCreate : function(){
7254 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7258 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7270 * Ext JS Library 1.1.1
7271 * Copyright(c) 2006-2007, Ext JS, LLC.
7273 * Originally Released Under LGPL - original licence link has changed is not relivant.
7276 * <script type="text/javascript">
7281 * @class Roo.grid.ColumnModel
7282 * @extends Roo.util.Observable
7283 * This is the default implementation of a ColumnModel used by the Grid. It defines
7284 * the columns in the grid.
7287 var colModel = new Roo.grid.ColumnModel([
7288 {header: "Ticker", width: 60, sortable: true, locked: true},
7289 {header: "Company Name", width: 150, sortable: true},
7290 {header: "Market Cap.", width: 100, sortable: true},
7291 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7292 {header: "Employees", width: 100, sortable: true, resizable: false}
7297 * The config options listed for this class are options which may appear in each
7298 * individual column definition.
7299 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7301 * @param {Object} config An Array of column config objects. See this class's
7302 * config objects for details.
7304 Roo.grid.ColumnModel = function(config){
7306 * The config passed into the constructor
7308 this.config = []; //config;
7311 // if no id, create one
7312 // if the column does not have a dataIndex mapping,
7313 // map it to the order it is in the config
7314 for(var i = 0, len = config.length; i < len; i++){
7315 this.addColumn(config[i]);
7320 * The width of columns which have no width specified (defaults to 100)
7323 this.defaultWidth = 100;
7326 * Default sortable of columns which have no sortable specified (defaults to false)
7329 this.defaultSortable = false;
7333 * @event widthchange
7334 * Fires when the width of a column changes.
7335 * @param {ColumnModel} this
7336 * @param {Number} columnIndex The column index
7337 * @param {Number} newWidth The new width
7339 "widthchange": true,
7341 * @event headerchange
7342 * Fires when the text of a header changes.
7343 * @param {ColumnModel} this
7344 * @param {Number} columnIndex The column index
7345 * @param {Number} newText The new header text
7347 "headerchange": true,
7349 * @event hiddenchange
7350 * Fires when a column is hidden or "unhidden".
7351 * @param {ColumnModel} this
7352 * @param {Number} columnIndex The column index
7353 * @param {Boolean} hidden true if hidden, false otherwise
7355 "hiddenchange": true,
7357 * @event columnmoved
7358 * Fires when a column is moved.
7359 * @param {ColumnModel} this
7360 * @param {Number} oldIndex
7361 * @param {Number} newIndex
7363 "columnmoved" : true,
7365 * @event columlockchange
7366 * Fires when a column's locked state is changed
7367 * @param {ColumnModel} this
7368 * @param {Number} colIndex
7369 * @param {Boolean} locked true if locked
7371 "columnlockchange" : true
7373 Roo.grid.ColumnModel.superclass.constructor.call(this);
7375 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7377 * @cfg {String} header The header text to display in the Grid view.
7380 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7381 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7382 * specified, the column's index is used as an index into the Record's data Array.
7385 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7386 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7389 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7390 * Defaults to the value of the {@link #defaultSortable} property.
7391 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7394 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7397 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7400 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7403 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7406 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7407 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7408 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7409 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7412 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7415 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7418 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7421 * @cfg {String} cursor (Optional)
7424 * @cfg {String} tooltip (Optional)
7427 * @cfg {Number} xs (Optional)
7430 * @cfg {Number} sm (Optional)
7433 * @cfg {Number} md (Optional)
7436 * @cfg {Number} lg (Optional)
7439 * Returns the id of the column at the specified index.
7440 * @param {Number} index The column index
7441 * @return {String} the id
7443 getColumnId : function(index){
7444 return this.config[index].id;
7448 * Returns the column for a specified id.
7449 * @param {String} id The column id
7450 * @return {Object} the column
7452 getColumnById : function(id){
7453 return this.lookup[id];
7458 * Returns the column Object for a specified dataIndex.
7459 * @param {String} dataIndex The column dataIndex
7460 * @return {Object|Boolean} the column or false if not found
7462 getColumnByDataIndex: function(dataIndex){
7463 var index = this.findColumnIndex(dataIndex);
7464 return index > -1 ? this.config[index] : false;
7468 * Returns the index for a specified column id.
7469 * @param {String} id The column id
7470 * @return {Number} the index, or -1 if not found
7472 getIndexById : function(id){
7473 for(var i = 0, len = this.config.length; i < len; i++){
7474 if(this.config[i].id == id){
7482 * Returns the index for a specified column dataIndex.
7483 * @param {String} dataIndex The column dataIndex
7484 * @return {Number} the index, or -1 if not found
7487 findColumnIndex : function(dataIndex){
7488 for(var i = 0, len = this.config.length; i < len; i++){
7489 if(this.config[i].dataIndex == dataIndex){
7497 moveColumn : function(oldIndex, newIndex){
7498 var c = this.config[oldIndex];
7499 this.config.splice(oldIndex, 1);
7500 this.config.splice(newIndex, 0, c);
7501 this.dataMap = null;
7502 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7505 isLocked : function(colIndex){
7506 return this.config[colIndex].locked === true;
7509 setLocked : function(colIndex, value, suppressEvent){
7510 if(this.isLocked(colIndex) == value){
7513 this.config[colIndex].locked = value;
7515 this.fireEvent("columnlockchange", this, colIndex, value);
7519 getTotalLockedWidth : function(){
7521 for(var i = 0; i < this.config.length; i++){
7522 if(this.isLocked(i) && !this.isHidden(i)){
7523 this.totalWidth += this.getColumnWidth(i);
7529 getLockedCount : function(){
7530 for(var i = 0, len = this.config.length; i < len; i++){
7531 if(!this.isLocked(i)){
7536 return this.config.length;
7540 * Returns the number of columns.
7543 getColumnCount : function(visibleOnly){
7544 if(visibleOnly === true){
7546 for(var i = 0, len = this.config.length; i < len; i++){
7547 if(!this.isHidden(i)){
7553 return this.config.length;
7557 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7558 * @param {Function} fn
7559 * @param {Object} scope (optional)
7560 * @return {Array} result
7562 getColumnsBy : function(fn, scope){
7564 for(var i = 0, len = this.config.length; i < len; i++){
7565 var c = this.config[i];
7566 if(fn.call(scope||this, c, i) === true){
7574 * Returns true if the specified column is sortable.
7575 * @param {Number} col The column index
7578 isSortable : function(col){
7579 if(typeof this.config[col].sortable == "undefined"){
7580 return this.defaultSortable;
7582 return this.config[col].sortable;
7586 * Returns the rendering (formatting) function defined for the column.
7587 * @param {Number} col The column index.
7588 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7590 getRenderer : function(col){
7591 if(!this.config[col].renderer){
7592 return Roo.grid.ColumnModel.defaultRenderer;
7594 return this.config[col].renderer;
7598 * Sets the rendering (formatting) function for a column.
7599 * @param {Number} col The column index
7600 * @param {Function} fn The function to use to process the cell's raw data
7601 * to return HTML markup for the grid view. The render function is called with
7602 * the following parameters:<ul>
7603 * <li>Data value.</li>
7604 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7605 * <li>css A CSS style string to apply to the table cell.</li>
7606 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7607 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7608 * <li>Row index</li>
7609 * <li>Column index</li>
7610 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7612 setRenderer : function(col, fn){
7613 this.config[col].renderer = fn;
7617 * Returns the width for the specified column.
7618 * @param {Number} col The column index
7621 getColumnWidth : function(col){
7622 return this.config[col].width * 1 || this.defaultWidth;
7626 * Sets the width for a column.
7627 * @param {Number} col The column index
7628 * @param {Number} width The new width
7630 setColumnWidth : function(col, width, suppressEvent){
7631 this.config[col].width = width;
7632 this.totalWidth = null;
7634 this.fireEvent("widthchange", this, col, width);
7639 * Returns the total width of all columns.
7640 * @param {Boolean} includeHidden True to include hidden column widths
7643 getTotalWidth : function(includeHidden){
7644 if(!this.totalWidth){
7645 this.totalWidth = 0;
7646 for(var i = 0, len = this.config.length; i < len; i++){
7647 if(includeHidden || !this.isHidden(i)){
7648 this.totalWidth += this.getColumnWidth(i);
7652 return this.totalWidth;
7656 * Returns the header for the specified column.
7657 * @param {Number} col The column index
7660 getColumnHeader : function(col){
7661 return this.config[col].header;
7665 * Sets the header for a column.
7666 * @param {Number} col The column index
7667 * @param {String} header The new header
7669 setColumnHeader : function(col, header){
7670 this.config[col].header = header;
7671 this.fireEvent("headerchange", this, col, header);
7675 * Returns the tooltip for the specified column.
7676 * @param {Number} col The column index
7679 getColumnTooltip : function(col){
7680 return this.config[col].tooltip;
7683 * Sets the tooltip for a column.
7684 * @param {Number} col The column index
7685 * @param {String} tooltip The new tooltip
7687 setColumnTooltip : function(col, tooltip){
7688 this.config[col].tooltip = tooltip;
7692 * Returns the dataIndex for the specified column.
7693 * @param {Number} col The column index
7696 getDataIndex : function(col){
7697 return this.config[col].dataIndex;
7701 * Sets the dataIndex for a column.
7702 * @param {Number} col The column index
7703 * @param {Number} dataIndex The new dataIndex
7705 setDataIndex : function(col, dataIndex){
7706 this.config[col].dataIndex = dataIndex;
7712 * Returns true if the cell is editable.
7713 * @param {Number} colIndex The column index
7714 * @param {Number} rowIndex The row index - this is nto actually used..?
7717 isCellEditable : function(colIndex, rowIndex){
7718 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7722 * Returns the editor defined for the cell/column.
7723 * return false or null to disable editing.
7724 * @param {Number} colIndex The column index
7725 * @param {Number} rowIndex The row index
7728 getCellEditor : function(colIndex, rowIndex){
7729 return this.config[colIndex].editor;
7733 * Sets if a column is editable.
7734 * @param {Number} col The column index
7735 * @param {Boolean} editable True if the column is editable
7737 setEditable : function(col, editable){
7738 this.config[col].editable = editable;
7743 * Returns true if the column is hidden.
7744 * @param {Number} colIndex The column index
7747 isHidden : function(colIndex){
7748 return this.config[colIndex].hidden;
7753 * Returns true if the column width cannot be changed
7755 isFixed : function(colIndex){
7756 return this.config[colIndex].fixed;
7760 * Returns true if the column can be resized
7763 isResizable : function(colIndex){
7764 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7767 * Sets if a column is hidden.
7768 * @param {Number} colIndex The column index
7769 * @param {Boolean} hidden True if the column is hidden
7771 setHidden : function(colIndex, hidden){
7772 this.config[colIndex].hidden = hidden;
7773 this.totalWidth = null;
7774 this.fireEvent("hiddenchange", this, colIndex, hidden);
7778 * Sets the editor for a column.
7779 * @param {Number} col The column index
7780 * @param {Object} editor The editor object
7782 setEditor : function(col, editor){
7783 this.config[col].editor = editor;
7786 * Add a column (experimental...) - defaults to adding to the end..
7787 * @param {Object} config
7789 addColumn : function(c)
7792 var i = this.config.length;
7795 if(typeof c.dataIndex == "undefined"){
7798 if(typeof c.renderer == "string"){
7799 c.renderer = Roo.util.Format[c.renderer];
7801 if(typeof c.id == "undefined"){
7804 if(c.editor && c.editor.xtype){
7805 c.editor = Roo.factory(c.editor, Roo.grid);
7807 if(c.editor && c.editor.isFormField){
7808 c.editor = new Roo.grid.GridEditor(c.editor);
7810 this.lookup[c.id] = c;
7815 Roo.grid.ColumnModel.defaultRenderer = function(value)
7817 if(typeof value == "object") {
7820 if(typeof value == "string" && value.length < 1){
7824 return String.format("{0}", value);
7827 // Alias for backwards compatibility
7828 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7831 * Ext JS Library 1.1.1
7832 * Copyright(c) 2006-2007, Ext JS, LLC.
7834 * Originally Released Under LGPL - original licence link has changed is not relivant.
7837 * <script type="text/javascript">
7841 * @class Roo.LoadMask
7842 * A simple utility class for generically masking elements while loading data. If the element being masked has
7843 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7844 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7845 * element's UpdateManager load indicator and will be destroyed after the initial load.
7847 * Create a new LoadMask
7848 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7849 * @param {Object} config The config object
7851 Roo.LoadMask = function(el, config){
7852 this.el = Roo.get(el);
7853 Roo.apply(this, config);
7855 this.store.on('beforeload', this.onBeforeLoad, this);
7856 this.store.on('load', this.onLoad, this);
7857 this.store.on('loadexception', this.onLoadException, this);
7858 this.removeMask = false;
7860 var um = this.el.getUpdateManager();
7861 um.showLoadIndicator = false; // disable the default indicator
7862 um.on('beforeupdate', this.onBeforeLoad, this);
7863 um.on('update', this.onLoad, this);
7864 um.on('failure', this.onLoad, this);
7865 this.removeMask = true;
7869 Roo.LoadMask.prototype = {
7871 * @cfg {Boolean} removeMask
7872 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7873 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7877 * The text to display in a centered loading message box (defaults to 'Loading...')
7881 * @cfg {String} msgCls
7882 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7884 msgCls : 'x-mask-loading',
7887 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7893 * Disables the mask to prevent it from being displayed
7895 disable : function(){
7896 this.disabled = true;
7900 * Enables the mask so that it can be displayed
7902 enable : function(){
7903 this.disabled = false;
7906 onLoadException : function()
7910 if (typeof(arguments[3]) != 'undefined') {
7911 Roo.MessageBox.alert("Error loading",arguments[3]);
7915 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7916 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7923 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7928 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7932 onBeforeLoad : function(){
7934 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7939 destroy : function(){
7941 this.store.un('beforeload', this.onBeforeLoad, this);
7942 this.store.un('load', this.onLoad, this);
7943 this.store.un('loadexception', this.onLoadException, this);
7945 var um = this.el.getUpdateManager();
7946 um.un('beforeupdate', this.onBeforeLoad, this);
7947 um.un('update', this.onLoad, this);
7948 um.un('failure', this.onLoad, this);
7959 * @class Roo.bootstrap.Table
7960 * @extends Roo.bootstrap.Component
7961 * Bootstrap Table class
7962 * @cfg {String} cls table class
7963 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7964 * @cfg {String} bgcolor Specifies the background color for a table
7965 * @cfg {Number} border Specifies whether the table cells should have borders or not
7966 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7967 * @cfg {Number} cellspacing Specifies the space between cells
7968 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7969 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7970 * @cfg {String} sortable Specifies that the table should be sortable
7971 * @cfg {String} summary Specifies a summary of the content of a table
7972 * @cfg {Number} width Specifies the width of a table
7973 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7975 * @cfg {boolean} striped Should the rows be alternative striped
7976 * @cfg {boolean} bordered Add borders to the table
7977 * @cfg {boolean} hover Add hover highlighting
7978 * @cfg {boolean} condensed Format condensed
7979 * @cfg {boolean} responsive Format condensed
7980 * @cfg {Boolean} loadMask (true|false) default false
7981 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7982 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7983 * @cfg {Boolean} rowSelection (true|false) default false
7984 * @cfg {Boolean} cellSelection (true|false) default false
7985 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7986 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7987 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7988 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7992 * Create a new Table
7993 * @param {Object} config The config object
7996 Roo.bootstrap.Table = function(config){
7997 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8002 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8003 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8004 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8005 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8007 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8009 this.sm.grid = this;
8010 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8011 this.sm = this.selModel;
8012 this.sm.xmodule = this.xmodule || false;
8015 if (this.cm && typeof(this.cm.config) == 'undefined') {
8016 this.colModel = new Roo.grid.ColumnModel(this.cm);
8017 this.cm = this.colModel;
8018 this.cm.xmodule = this.xmodule || false;
8021 this.store= Roo.factory(this.store, Roo.data);
8022 this.ds = this.store;
8023 this.ds.xmodule = this.xmodule || false;
8026 if (this.footer && this.store) {
8027 this.footer.dataSource = this.ds;
8028 this.footer = Roo.factory(this.footer);
8035 * Fires when a cell is clicked
8036 * @param {Roo.bootstrap.Table} this
8037 * @param {Roo.Element} el
8038 * @param {Number} rowIndex
8039 * @param {Number} columnIndex
8040 * @param {Roo.EventObject} e
8044 * @event celldblclick
8045 * Fires when a cell is double clicked
8046 * @param {Roo.bootstrap.Table} this
8047 * @param {Roo.Element} el
8048 * @param {Number} rowIndex
8049 * @param {Number} columnIndex
8050 * @param {Roo.EventObject} e
8052 "celldblclick" : true,
8055 * Fires when a row is clicked
8056 * @param {Roo.bootstrap.Table} this
8057 * @param {Roo.Element} el
8058 * @param {Number} rowIndex
8059 * @param {Roo.EventObject} e
8063 * @event rowdblclick
8064 * Fires when a row is double clicked
8065 * @param {Roo.bootstrap.Table} this
8066 * @param {Roo.Element} el
8067 * @param {Number} rowIndex
8068 * @param {Roo.EventObject} e
8070 "rowdblclick" : true,
8073 * Fires when a mouseover occur
8074 * @param {Roo.bootstrap.Table} this
8075 * @param {Roo.Element} el
8076 * @param {Number} rowIndex
8077 * @param {Number} columnIndex
8078 * @param {Roo.EventObject} e
8083 * Fires when a mouseout occur
8084 * @param {Roo.bootstrap.Table} this
8085 * @param {Roo.Element} el
8086 * @param {Number} rowIndex
8087 * @param {Number} columnIndex
8088 * @param {Roo.EventObject} e
8093 * Fires when a row is rendered, so you can change add a style to it.
8094 * @param {Roo.bootstrap.Table} this
8095 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8099 * @event rowsrendered
8100 * Fires when all the rows have been rendered
8101 * @param {Roo.bootstrap.Table} this
8103 'rowsrendered' : true,
8105 * @event contextmenu
8106 * The raw contextmenu event for the entire grid.
8107 * @param {Roo.EventObject} e
8109 "contextmenu" : true,
8111 * @event rowcontextmenu
8112 * Fires when a row is right clicked
8113 * @param {Roo.bootstrap.Table} this
8114 * @param {Number} rowIndex
8115 * @param {Roo.EventObject} e
8117 "rowcontextmenu" : true,
8119 * @event cellcontextmenu
8120 * Fires when a cell is right clicked
8121 * @param {Roo.bootstrap.Table} this
8122 * @param {Number} rowIndex
8123 * @param {Number} cellIndex
8124 * @param {Roo.EventObject} e
8126 "cellcontextmenu" : true,
8128 * @event headercontextmenu
8129 * Fires when a header is right clicked
8130 * @param {Roo.bootstrap.Table} this
8131 * @param {Number} columnIndex
8132 * @param {Roo.EventObject} e
8134 "headercontextmenu" : true
8138 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8164 rowSelection : false,
8165 cellSelection : false,
8168 // Roo.Element - the tbody
8170 // Roo.Element - thead element
8173 container: false, // used by gridpanel...
8179 auto_hide_footer : false,
8181 getAutoCreate : function()
8183 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8190 if (this.scrollBody) {
8191 cfg.cls += ' table-body-fixed';
8194 cfg.cls += ' table-striped';
8198 cfg.cls += ' table-hover';
8200 if (this.bordered) {
8201 cfg.cls += ' table-bordered';
8203 if (this.condensed) {
8204 cfg.cls += ' table-condensed';
8206 if (this.responsive) {
8207 cfg.cls += ' table-responsive';
8211 cfg.cls+= ' ' +this.cls;
8214 // this lot should be simplifed...
8227 ].forEach(function(k) {
8235 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8238 if(this.store || this.cm){
8239 if(this.headerShow){
8240 cfg.cn.push(this.renderHeader());
8243 cfg.cn.push(this.renderBody());
8245 if(this.footerShow){
8246 cfg.cn.push(this.renderFooter());
8248 // where does this come from?
8249 //cfg.cls+= ' TableGrid';
8252 return { cn : [ cfg ] };
8255 initEvents : function()
8257 if(!this.store || !this.cm){
8260 if (this.selModel) {
8261 this.selModel.initEvents();
8265 //Roo.log('initEvents with ds!!!!');
8267 this.mainBody = this.el.select('tbody', true).first();
8268 this.mainHead = this.el.select('thead', true).first();
8269 this.mainFoot = this.el.select('tfoot', true).first();
8274 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8275 e.on('click', this.sort, this);
8278 this.mainBody.on("click", this.onClick, this);
8279 this.mainBody.on("dblclick", this.onDblClick, this);
8281 // why is this done????? = it breaks dialogs??
8282 //this.parent().el.setStyle('position', 'relative');
8286 this.footer.parentId = this.id;
8287 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8290 this.el.select('tfoot tr td').first().addClass('hide');
8295 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8298 this.store.on('load', this.onLoad, this);
8299 this.store.on('beforeload', this.onBeforeLoad, this);
8300 this.store.on('update', this.onUpdate, this);
8301 this.store.on('add', this.onAdd, this);
8302 this.store.on("clear", this.clear, this);
8304 this.el.on("contextmenu", this.onContextMenu, this);
8306 this.mainBody.on('scroll', this.onBodyScroll, this);
8308 this.cm.on("headerchange", this.onHeaderChange, this);
8310 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8314 onContextMenu : function(e, t)
8316 this.processEvent("contextmenu", e);
8319 processEvent : function(name, e)
8321 if (name != 'touchstart' ) {
8322 this.fireEvent(name, e);
8325 var t = e.getTarget();
8327 var cell = Roo.get(t);
8333 if(cell.findParent('tfoot', false, true)){
8337 if(cell.findParent('thead', false, true)){
8339 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8340 cell = Roo.get(t).findParent('th', false, true);
8342 Roo.log("failed to find th in thead?");
8343 Roo.log(e.getTarget());
8348 var cellIndex = cell.dom.cellIndex;
8350 var ename = name == 'touchstart' ? 'click' : name;
8351 this.fireEvent("header" + ename, this, cellIndex, e);
8356 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8357 cell = Roo.get(t).findParent('td', false, true);
8359 Roo.log("failed to find th in tbody?");
8360 Roo.log(e.getTarget());
8365 var row = cell.findParent('tr', false, true);
8366 var cellIndex = cell.dom.cellIndex;
8367 var rowIndex = row.dom.rowIndex - 1;
8371 this.fireEvent("row" + name, this, rowIndex, e);
8375 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8381 onMouseover : function(e, el)
8383 var cell = Roo.get(el);
8389 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8390 cell = cell.findParent('td', false, true);
8393 var row = cell.findParent('tr', false, true);
8394 var cellIndex = cell.dom.cellIndex;
8395 var rowIndex = row.dom.rowIndex - 1; // start from 0
8397 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8401 onMouseout : function(e, el)
8403 var cell = Roo.get(el);
8409 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8410 cell = cell.findParent('td', false, true);
8413 var row = cell.findParent('tr', false, true);
8414 var cellIndex = cell.dom.cellIndex;
8415 var rowIndex = row.dom.rowIndex - 1; // start from 0
8417 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8421 onClick : function(e, el)
8423 var cell = Roo.get(el);
8425 if(!cell || (!this.cellSelection && !this.rowSelection)){
8429 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8430 cell = cell.findParent('td', false, true);
8433 if(!cell || typeof(cell) == 'undefined'){
8437 var row = cell.findParent('tr', false, true);
8439 if(!row || typeof(row) == 'undefined'){
8443 var cellIndex = cell.dom.cellIndex;
8444 var rowIndex = this.getRowIndex(row);
8446 // why??? - should these not be based on SelectionModel?
8447 if(this.cellSelection){
8448 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8451 if(this.rowSelection){
8452 this.fireEvent('rowclick', this, row, rowIndex, e);
8458 onDblClick : function(e,el)
8460 var cell = Roo.get(el);
8462 if(!cell || (!this.cellSelection && !this.rowSelection)){
8466 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8467 cell = cell.findParent('td', false, true);
8470 if(!cell || typeof(cell) == 'undefined'){
8474 var row = cell.findParent('tr', false, true);
8476 if(!row || typeof(row) == 'undefined'){
8480 var cellIndex = cell.dom.cellIndex;
8481 var rowIndex = this.getRowIndex(row);
8483 if(this.cellSelection){
8484 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8487 if(this.rowSelection){
8488 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8492 sort : function(e,el)
8494 var col = Roo.get(el);
8496 if(!col.hasClass('sortable')){
8500 var sort = col.attr('sort');
8503 if(col.select('i', true).first().hasClass('fa-arrow-up')){
8507 this.store.sortInfo = {field : sort, direction : dir};
8510 Roo.log("calling footer first");
8511 this.footer.onClick('first');
8514 this.store.load({ params : { start : 0 } });
8518 renderHeader : function()
8526 this.totalWidth = 0;
8528 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8530 var config = cm.config[i];
8534 cls : 'x-hcol-' + i,
8536 html: cm.getColumnHeader(i)
8541 if(typeof(config.sortable) != 'undefined' && config.sortable){
8543 c.html = '<i class="fa"></i>' + c.html;
8546 // could use BS4 hidden-..-down
8548 if(typeof(config.lgHeader) != 'undefined'){
8549 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8552 if(typeof(config.mdHeader) != 'undefined'){
8553 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8556 if(typeof(config.smHeader) != 'undefined'){
8557 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8560 if(typeof(config.xsHeader) != 'undefined'){
8561 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8568 if(typeof(config.tooltip) != 'undefined'){
8569 c.tooltip = config.tooltip;
8572 if(typeof(config.colspan) != 'undefined'){
8573 c.colspan = config.colspan;
8576 if(typeof(config.hidden) != 'undefined' && config.hidden){
8577 c.style += ' display:none;';
8580 if(typeof(config.dataIndex) != 'undefined'){
8581 c.sort = config.dataIndex;
8586 if(typeof(config.align) != 'undefined' && config.align.length){
8587 c.style += ' text-align:' + config.align + ';';
8590 if(typeof(config.width) != 'undefined'){
8591 c.style += ' width:' + config.width + 'px;';
8592 this.totalWidth += config.width;
8594 this.totalWidth += 100; // assume minimum of 100 per column?
8597 if(typeof(config.cls) != 'undefined'){
8598 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8601 ['xs','sm','md','lg'].map(function(size){
8603 if(typeof(config[size]) == 'undefined'){
8607 if (!config[size]) { // 0 = hidden
8608 // BS 4 '0' is treated as hide that column and below.
8609 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8613 c.cls += ' col-' + size + '-' + config[size] + (
8614 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8626 renderBody : function()
8636 colspan : this.cm.getColumnCount()
8646 renderFooter : function()
8656 colspan : this.cm.getColumnCount()
8670 // Roo.log('ds onload');
8675 var ds = this.store;
8677 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8678 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8679 if (_this.store.sortInfo) {
8681 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8682 e.select('i', true).addClass(['fa-arrow-up']);
8685 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8686 e.select('i', true).addClass(['fa-arrow-down']);
8691 var tbody = this.mainBody;
8693 if(ds.getCount() > 0){
8694 ds.data.each(function(d,rowIndex){
8695 var row = this.renderRow(cm, ds, rowIndex);
8697 tbody.createChild(row);
8701 if(row.cellObjects.length){
8702 Roo.each(row.cellObjects, function(r){
8703 _this.renderCellObject(r);
8710 var tfoot = this.el.select('tfoot', true).first();
8712 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8714 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8716 var total = this.ds.getTotalCount();
8718 if(this.footer.pageSize < total){
8719 this.mainFoot.show();
8723 Roo.each(this.el.select('tbody td', true).elements, function(e){
8724 e.on('mouseover', _this.onMouseover, _this);
8727 Roo.each(this.el.select('tbody td', true).elements, function(e){
8728 e.on('mouseout', _this.onMouseout, _this);
8730 this.fireEvent('rowsrendered', this);
8736 onUpdate : function(ds,record)
8738 this.refreshRow(record);
8742 onRemove : function(ds, record, index, isUpdate){
8743 if(isUpdate !== true){
8744 this.fireEvent("beforerowremoved", this, index, record);
8746 var bt = this.mainBody.dom;
8748 var rows = this.el.select('tbody > tr', true).elements;
8750 if(typeof(rows[index]) != 'undefined'){
8751 bt.removeChild(rows[index].dom);
8754 // if(bt.rows[index]){
8755 // bt.removeChild(bt.rows[index]);
8758 if(isUpdate !== true){
8759 //this.stripeRows(index);
8760 //this.syncRowHeights(index, index);
8762 this.fireEvent("rowremoved", this, index, record);
8766 onAdd : function(ds, records, rowIndex)
8768 //Roo.log('on Add called');
8769 // - note this does not handle multiple adding very well..
8770 var bt = this.mainBody.dom;
8771 for (var i =0 ; i < records.length;i++) {
8772 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8773 //Roo.log(records[i]);
8774 //Roo.log(this.store.getAt(rowIndex+i));
8775 this.insertRow(this.store, rowIndex + i, false);
8782 refreshRow : function(record){
8783 var ds = this.store, index;
8784 if(typeof record == 'number'){
8786 record = ds.getAt(index);
8788 index = ds.indexOf(record);
8790 return; // should not happen - but seems to
8793 this.insertRow(ds, index, true);
8795 this.onRemove(ds, record, index+1, true);
8797 //this.syncRowHeights(index, index);
8799 this.fireEvent("rowupdated", this, index, record);
8802 insertRow : function(dm, rowIndex, isUpdate){
8805 this.fireEvent("beforerowsinserted", this, rowIndex);
8807 //var s = this.getScrollState();
8808 var row = this.renderRow(this.cm, this.store, rowIndex);
8809 // insert before rowIndex..
8810 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8814 if(row.cellObjects.length){
8815 Roo.each(row.cellObjects, function(r){
8816 _this.renderCellObject(r);
8821 this.fireEvent("rowsinserted", this, rowIndex);
8822 //this.syncRowHeights(firstRow, lastRow);
8823 //this.stripeRows(firstRow);
8830 getRowDom : function(rowIndex)
8832 var rows = this.el.select('tbody > tr', true).elements;
8834 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8837 // returns the object tree for a tr..
8840 renderRow : function(cm, ds, rowIndex)
8842 var d = ds.getAt(rowIndex);
8846 cls : 'x-row-' + rowIndex,
8850 var cellObjects = [];
8852 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8853 var config = cm.config[i];
8855 var renderer = cm.getRenderer(i);
8859 if(typeof(renderer) !== 'undefined'){
8860 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8862 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8863 // and are rendered into the cells after the row is rendered - using the id for the element.
8865 if(typeof(value) === 'object'){
8875 rowIndex : rowIndex,
8880 this.fireEvent('rowclass', this, rowcfg);
8884 tooltip : (typeof(value) === 'object') ? '' : value,
8885 cls : rowcfg.rowClass + ' x-col-' + i,
8887 html: (typeof(value) === 'object') ? '' : value
8894 if(typeof(config.colspan) != 'undefined'){
8895 td.colspan = config.colspan;
8898 if(typeof(config.hidden) != 'undefined' && config.hidden){
8899 td.style += ' display:none;';
8902 if(typeof(config.align) != 'undefined' && config.align.length){
8903 td.style += ' text-align:' + config.align + ';';
8905 if(typeof(config.valign) != 'undefined' && config.valign.length){
8906 td.style += ' vertical-align:' + config.valign + ';';
8909 if(typeof(config.width) != 'undefined'){
8910 td.style += ' width:' + config.width + 'px;';
8913 if(typeof(config.cursor) != 'undefined'){
8914 td.style += ' cursor:' + config.cursor + ';';
8917 if(typeof(config.cls) != 'undefined'){
8918 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8921 ['xs','sm','md','lg'].map(function(size){
8923 if(typeof(config[size]) == 'undefined'){
8929 if (!config[size]) { // 0 = hidden
8930 // BS 4 '0' is treated as hide that column and below.
8931 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8935 td.cls += ' col-' + size + '-' + config[size] + (
8936 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8946 row.cellObjects = cellObjects;
8954 onBeforeLoad : function()
8963 this.el.select('tbody', true).first().dom.innerHTML = '';
8966 * Show or hide a row.
8967 * @param {Number} rowIndex to show or hide
8968 * @param {Boolean} state hide
8970 setRowVisibility : function(rowIndex, state)
8972 var bt = this.mainBody.dom;
8974 var rows = this.el.select('tbody > tr', true).elements;
8976 if(typeof(rows[rowIndex]) == 'undefined'){
8979 rows[rowIndex].dom.style.display = state ? '' : 'none';
8983 getSelectionModel : function(){
8985 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8987 return this.selModel;
8990 * Render the Roo.bootstrap object from renderder
8992 renderCellObject : function(r)
8996 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8998 var t = r.cfg.render(r.container);
9001 Roo.each(r.cfg.cn, function(c){
9003 container: t.getChildContainer(),
9006 _this.renderCellObject(child);
9011 getRowIndex : function(row)
9015 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9026 * Returns the grid's underlying element = used by panel.Grid
9027 * @return {Element} The element
9029 getGridEl : function(){
9033 * Forces a resize - used by panel.Grid
9034 * @return {Element} The element
9036 autoSize : function()
9038 //var ctr = Roo.get(this.container.dom.parentElement);
9039 var ctr = Roo.get(this.el.dom);
9041 var thd = this.getGridEl().select('thead',true).first();
9042 var tbd = this.getGridEl().select('tbody', true).first();
9043 var tfd = this.getGridEl().select('tfoot', true).first();
9045 var cw = ctr.getWidth();
9046 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9050 tbd.setWidth(ctr.getWidth());
9051 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9052 // this needs fixing for various usage - currently only hydra job advers I think..
9054 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9056 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9059 cw = Math.max(cw, this.totalWidth);
9060 this.getGridEl().select('tbody tr',true).setWidth(cw);
9062 // resize 'expandable coloumn?
9064 return; // we doe not have a view in this design..
9067 onBodyScroll: function()
9069 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9071 this.mainHead.setStyle({
9072 'position' : 'relative',
9073 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9079 var scrollHeight = this.mainBody.dom.scrollHeight;
9081 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9083 var height = this.mainBody.getHeight();
9085 if(scrollHeight - height == scrollTop) {
9087 var total = this.ds.getTotalCount();
9089 if(this.footer.cursor + this.footer.pageSize < total){
9091 this.footer.ds.load({
9093 start : this.footer.cursor + this.footer.pageSize,
9094 limit : this.footer.pageSize
9104 onHeaderChange : function()
9106 var header = this.renderHeader();
9107 var table = this.el.select('table', true).first();
9109 this.mainHead.remove();
9110 this.mainHead = table.createChild(header, this.mainBody, false);
9112 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9113 e.on('click', this.sort, this);
9119 onHiddenChange : function(colModel, colIndex, hidden)
9121 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9122 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9124 this.CSS.updateRule(thSelector, "display", "");
9125 this.CSS.updateRule(tdSelector, "display", "");
9128 this.CSS.updateRule(thSelector, "display", "none");
9129 this.CSS.updateRule(tdSelector, "display", "none");
9132 this.onHeaderChange();
9136 setColumnWidth: function(col_index, width)
9138 // width = "md-2 xs-2..."
9139 if(!this.colModel.config[col_index]) {
9143 var w = width.split(" ");
9145 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9147 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9150 for(var j = 0; j < w.length; j++) {
9156 var size_cls = w[j].split("-");
9158 if(!Number.isInteger(size_cls[1] * 1)) {
9162 if(!this.colModel.config[col_index][size_cls[0]]) {
9166 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9170 h_row[0].classList.replace(
9171 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9172 "col-"+size_cls[0]+"-"+size_cls[1]
9175 for(var i = 0; i < rows.length; i++) {
9177 var size_cls = w[j].split("-");
9179 if(!Number.isInteger(size_cls[1] * 1)) {
9183 if(!this.colModel.config[col_index][size_cls[0]]) {
9187 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9191 rows[i].classList.replace(
9192 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9193 "col-"+size_cls[0]+"-"+size_cls[1]
9197 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9212 * @class Roo.bootstrap.TableCell
9213 * @extends Roo.bootstrap.Component
9214 * Bootstrap TableCell class
9215 * @cfg {String} html cell contain text
9216 * @cfg {String} cls cell class
9217 * @cfg {String} tag cell tag (td|th) default td
9218 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9219 * @cfg {String} align Aligns the content in a cell
9220 * @cfg {String} axis Categorizes cells
9221 * @cfg {String} bgcolor Specifies the background color of a cell
9222 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9223 * @cfg {Number} colspan Specifies the number of columns a cell should span
9224 * @cfg {String} headers Specifies one or more header cells a cell is related to
9225 * @cfg {Number} height Sets the height of a cell
9226 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9227 * @cfg {Number} rowspan Sets the number of rows a cell should span
9228 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9229 * @cfg {String} valign Vertical aligns the content in a cell
9230 * @cfg {Number} width Specifies the width of a cell
9233 * Create a new TableCell
9234 * @param {Object} config The config object
9237 Roo.bootstrap.TableCell = function(config){
9238 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9241 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9261 getAutoCreate : function(){
9262 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9282 cfg.align=this.align
9288 cfg.bgcolor=this.bgcolor
9291 cfg.charoff=this.charoff
9294 cfg.colspan=this.colspan
9297 cfg.headers=this.headers
9300 cfg.height=this.height
9303 cfg.nowrap=this.nowrap
9306 cfg.rowspan=this.rowspan
9309 cfg.scope=this.scope
9312 cfg.valign=this.valign
9315 cfg.width=this.width
9334 * @class Roo.bootstrap.TableRow
9335 * @extends Roo.bootstrap.Component
9336 * Bootstrap TableRow class
9337 * @cfg {String} cls row class
9338 * @cfg {String} align Aligns the content in a table row
9339 * @cfg {String} bgcolor Specifies a background color for a table row
9340 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9341 * @cfg {String} valign Vertical aligns the content in a table row
9344 * Create a new TableRow
9345 * @param {Object} config The config object
9348 Roo.bootstrap.TableRow = function(config){
9349 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9352 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9360 getAutoCreate : function(){
9361 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9371 cfg.align = this.align;
9374 cfg.bgcolor = this.bgcolor;
9377 cfg.charoff = this.charoff;
9380 cfg.valign = this.valign;
9398 * @class Roo.bootstrap.TableBody
9399 * @extends Roo.bootstrap.Component
9400 * Bootstrap TableBody class
9401 * @cfg {String} cls element class
9402 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9403 * @cfg {String} align Aligns the content inside the element
9404 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9405 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9408 * Create a new TableBody
9409 * @param {Object} config The config object
9412 Roo.bootstrap.TableBody = function(config){
9413 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9416 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9424 getAutoCreate : function(){
9425 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9439 cfg.align = this.align;
9442 cfg.charoff = this.charoff;
9445 cfg.valign = this.valign;
9452 // initEvents : function()
9459 // this.store = Roo.factory(this.store, Roo.data);
9460 // this.store.on('load', this.onLoad, this);
9462 // this.store.load();
9466 // onLoad: function ()
9468 // this.fireEvent('load', this);
9478 * Ext JS Library 1.1.1
9479 * Copyright(c) 2006-2007, Ext JS, LLC.
9481 * Originally Released Under LGPL - original licence link has changed is not relivant.
9484 * <script type="text/javascript">
9487 // as we use this in bootstrap.
9488 Roo.namespace('Roo.form');
9490 * @class Roo.form.Action
9491 * Internal Class used to handle form actions
9493 * @param {Roo.form.BasicForm} el The form element or its id
9494 * @param {Object} config Configuration options
9499 // define the action interface
9500 Roo.form.Action = function(form, options){
9502 this.options = options || {};
9505 * Client Validation Failed
9508 Roo.form.Action.CLIENT_INVALID = 'client';
9510 * Server Validation Failed
9513 Roo.form.Action.SERVER_INVALID = 'server';
9515 * Connect to Server Failed
9518 Roo.form.Action.CONNECT_FAILURE = 'connect';
9520 * Reading Data from Server Failed
9523 Roo.form.Action.LOAD_FAILURE = 'load';
9525 Roo.form.Action.prototype = {
9527 failureType : undefined,
9528 response : undefined,
9532 run : function(options){
9537 success : function(response){
9542 handleResponse : function(response){
9546 // default connection failure
9547 failure : function(response){
9549 this.response = response;
9550 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9551 this.form.afterAction(this, false);
9554 processResponse : function(response){
9555 this.response = response;
9556 if(!response.responseText){
9559 this.result = this.handleResponse(response);
9563 // utility functions used internally
9564 getUrl : function(appendParams){
9565 var url = this.options.url || this.form.url || this.form.el.dom.action;
9567 var p = this.getParams();
9569 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9575 getMethod : function(){
9576 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9579 getParams : function(){
9580 var bp = this.form.baseParams;
9581 var p = this.options.params;
9583 if(typeof p == "object"){
9584 p = Roo.urlEncode(Roo.applyIf(p, bp));
9585 }else if(typeof p == 'string' && bp){
9586 p += '&' + Roo.urlEncode(bp);
9589 p = Roo.urlEncode(bp);
9594 createCallback : function(){
9596 success: this.success,
9597 failure: this.failure,
9599 timeout: (this.form.timeout*1000),
9600 upload: this.form.fileUpload ? this.success : undefined
9605 Roo.form.Action.Submit = function(form, options){
9606 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9609 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9612 haveProgress : false,
9613 uploadComplete : false,
9615 // uploadProgress indicator.
9616 uploadProgress : function()
9618 if (!this.form.progressUrl) {
9622 if (!this.haveProgress) {
9623 Roo.MessageBox.progress("Uploading", "Uploading");
9625 if (this.uploadComplete) {
9626 Roo.MessageBox.hide();
9630 this.haveProgress = true;
9632 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9634 var c = new Roo.data.Connection();
9636 url : this.form.progressUrl,
9641 success : function(req){
9642 //console.log(data);
9646 rdata = Roo.decode(req.responseText)
9648 Roo.log("Invalid data from server..");
9652 if (!rdata || !rdata.success) {
9654 Roo.MessageBox.alert(Roo.encode(rdata));
9657 var data = rdata.data;
9659 if (this.uploadComplete) {
9660 Roo.MessageBox.hide();
9665 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9666 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9669 this.uploadProgress.defer(2000,this);
9672 failure: function(data) {
9673 Roo.log('progress url failed ');
9684 // run get Values on the form, so it syncs any secondary forms.
9685 this.form.getValues();
9687 var o = this.options;
9688 var method = this.getMethod();
9689 var isPost = method == 'POST';
9690 if(o.clientValidation === false || this.form.isValid()){
9692 if (this.form.progressUrl) {
9693 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9694 (new Date() * 1) + '' + Math.random());
9699 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9700 form:this.form.el.dom,
9701 url:this.getUrl(!isPost),
9703 params:isPost ? this.getParams() : null,
9704 isUpload: this.form.fileUpload,
9705 formData : this.form.formData
9708 this.uploadProgress();
9710 }else if (o.clientValidation !== false){ // client validation failed
9711 this.failureType = Roo.form.Action.CLIENT_INVALID;
9712 this.form.afterAction(this, false);
9716 success : function(response)
9718 this.uploadComplete= true;
9719 if (this.haveProgress) {
9720 Roo.MessageBox.hide();
9724 var result = this.processResponse(response);
9725 if(result === true || result.success){
9726 this.form.afterAction(this, true);
9730 this.form.markInvalid(result.errors);
9731 this.failureType = Roo.form.Action.SERVER_INVALID;
9733 this.form.afterAction(this, false);
9735 failure : function(response)
9737 this.uploadComplete= true;
9738 if (this.haveProgress) {
9739 Roo.MessageBox.hide();
9742 this.response = response;
9743 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9744 this.form.afterAction(this, false);
9747 handleResponse : function(response){
9748 if(this.form.errorReader){
9749 var rs = this.form.errorReader.read(response);
9752 for(var i = 0, len = rs.records.length; i < len; i++) {
9753 var r = rs.records[i];
9757 if(errors.length < 1){
9761 success : rs.success,
9767 ret = Roo.decode(response.responseText);
9771 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9781 Roo.form.Action.Load = function(form, options){
9782 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9783 this.reader = this.form.reader;
9786 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9791 Roo.Ajax.request(Roo.apply(
9792 this.createCallback(), {
9793 method:this.getMethod(),
9794 url:this.getUrl(false),
9795 params:this.getParams()
9799 success : function(response){
9801 var result = this.processResponse(response);
9802 if(result === true || !result.success || !result.data){
9803 this.failureType = Roo.form.Action.LOAD_FAILURE;
9804 this.form.afterAction(this, false);
9807 this.form.clearInvalid();
9808 this.form.setValues(result.data);
9809 this.form.afterAction(this, true);
9812 handleResponse : function(response){
9813 if(this.form.reader){
9814 var rs = this.form.reader.read(response);
9815 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9817 success : rs.success,
9821 return Roo.decode(response.responseText);
9825 Roo.form.Action.ACTION_TYPES = {
9826 'load' : Roo.form.Action.Load,
9827 'submit' : Roo.form.Action.Submit
9836 * @class Roo.bootstrap.Form
9837 * @extends Roo.bootstrap.Component
9838 * Bootstrap Form class
9839 * @cfg {String} method GET | POST (default POST)
9840 * @cfg {String} labelAlign top | left (default top)
9841 * @cfg {String} align left | right - for navbars
9842 * @cfg {Boolean} loadMask load mask when submit (default true)
9847 * @param {Object} config The config object
9851 Roo.bootstrap.Form = function(config){
9853 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9855 Roo.bootstrap.Form.popover.apply();
9859 * @event clientvalidation
9860 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9861 * @param {Form} this
9862 * @param {Boolean} valid true if the form has passed client-side validation
9864 clientvalidation: true,
9866 * @event beforeaction
9867 * Fires before any action is performed. Return false to cancel the action.
9868 * @param {Form} this
9869 * @param {Action} action The action to be performed
9873 * @event actionfailed
9874 * Fires when an action fails.
9875 * @param {Form} this
9876 * @param {Action} action The action that failed
9878 actionfailed : true,
9880 * @event actioncomplete
9881 * Fires when an action is completed.
9882 * @param {Form} this
9883 * @param {Action} action The action that completed
9885 actioncomplete : true
9889 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9892 * @cfg {String} method
9893 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9898 * The URL to use for form actions if one isn't supplied in the action options.
9901 * @cfg {Boolean} fileUpload
9902 * Set to true if this form is a file upload.
9906 * @cfg {Object} baseParams
9907 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9911 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9915 * @cfg {Sting} align (left|right) for navbar forms
9920 activeAction : null,
9923 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9924 * element by passing it or its id or mask the form itself by passing in true.
9927 waitMsgTarget : false,
9932 * @cfg {Boolean} errorMask (true|false) default false
9937 * @cfg {Number} maskOffset Default 100
9942 * @cfg {Boolean} maskBody
9946 getAutoCreate : function(){
9950 method : this.method || 'POST',
9951 id : this.id || Roo.id(),
9954 if (this.parent().xtype.match(/^Nav/)) {
9955 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9959 if (this.labelAlign == 'left' ) {
9960 cfg.cls += ' form-horizontal';
9966 initEvents : function()
9968 this.el.on('submit', this.onSubmit, this);
9969 // this was added as random key presses on the form where triggering form submit.
9970 this.el.on('keypress', function(e) {
9971 if (e.getCharCode() != 13) {
9974 // we might need to allow it for textareas.. and some other items.
9975 // check e.getTarget().
9977 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9981 Roo.log("keypress blocked");
9989 onSubmit : function(e){
9994 * Returns true if client-side validation on the form is successful.
9997 isValid : function(){
9998 var items = this.getItems();
10000 var target = false;
10002 items.each(function(f){
10008 Roo.log('invalid field: ' + f.name);
10012 if(!target && f.el.isVisible(true)){
10018 if(this.errorMask && !valid){
10019 Roo.bootstrap.Form.popover.mask(this, target);
10026 * Returns true if any fields in this form have changed since their original load.
10029 isDirty : function(){
10031 var items = this.getItems();
10032 items.each(function(f){
10042 * Performs a predefined action (submit or load) or custom actions you define on this form.
10043 * @param {String} actionName The name of the action type
10044 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10045 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10046 * accept other config options):
10048 Property Type Description
10049 ---------------- --------------- ----------------------------------------------------------------------------------
10050 url String The url for the action (defaults to the form's url)
10051 method String The form method to use (defaults to the form's method, or POST if not defined)
10052 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10053 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10054 validate the form on the client (defaults to false)
10056 * @return {BasicForm} this
10058 doAction : function(action, options){
10059 if(typeof action == 'string'){
10060 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10062 if(this.fireEvent('beforeaction', this, action) !== false){
10063 this.beforeAction(action);
10064 action.run.defer(100, action);
10070 beforeAction : function(action){
10071 var o = action.options;
10076 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10078 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10081 // not really supported yet.. ??
10083 //if(this.waitMsgTarget === true){
10084 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10085 //}else if(this.waitMsgTarget){
10086 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10087 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10089 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10095 afterAction : function(action, success){
10096 this.activeAction = null;
10097 var o = action.options;
10102 Roo.get(document.body).unmask();
10108 //if(this.waitMsgTarget === true){
10109 // this.el.unmask();
10110 //}else if(this.waitMsgTarget){
10111 // this.waitMsgTarget.unmask();
10113 // Roo.MessageBox.updateProgress(1);
10114 // Roo.MessageBox.hide();
10121 Roo.callback(o.success, o.scope, [this, action]);
10122 this.fireEvent('actioncomplete', this, action);
10126 // failure condition..
10127 // we have a scenario where updates need confirming.
10128 // eg. if a locking scenario exists..
10129 // we look for { errors : { needs_confirm : true }} in the response.
10131 (typeof(action.result) != 'undefined') &&
10132 (typeof(action.result.errors) != 'undefined') &&
10133 (typeof(action.result.errors.needs_confirm) != 'undefined')
10136 Roo.log("not supported yet");
10139 Roo.MessageBox.confirm(
10140 "Change requires confirmation",
10141 action.result.errorMsg,
10146 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10156 Roo.callback(o.failure, o.scope, [this, action]);
10157 // show an error message if no failed handler is set..
10158 if (!this.hasListener('actionfailed')) {
10159 Roo.log("need to add dialog support");
10161 Roo.MessageBox.alert("Error",
10162 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10163 action.result.errorMsg :
10164 "Saving Failed, please check your entries or try again"
10169 this.fireEvent('actionfailed', this, action);
10174 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10175 * @param {String} id The value to search for
10178 findField : function(id){
10179 var items = this.getItems();
10180 var field = items.get(id);
10182 items.each(function(f){
10183 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10190 return field || null;
10193 * Mark fields in this form invalid in bulk.
10194 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10195 * @return {BasicForm} this
10197 markInvalid : function(errors){
10198 if(errors instanceof Array){
10199 for(var i = 0, len = errors.length; i < len; i++){
10200 var fieldError = errors[i];
10201 var f = this.findField(fieldError.id);
10203 f.markInvalid(fieldError.msg);
10209 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10210 field.markInvalid(errors[id]);
10214 //Roo.each(this.childForms || [], function (f) {
10215 // f.markInvalid(errors);
10222 * Set values for fields in this form in bulk.
10223 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10224 * @return {BasicForm} this
10226 setValues : function(values){
10227 if(values instanceof Array){ // array of objects
10228 for(var i = 0, len = values.length; i < len; i++){
10230 var f = this.findField(v.id);
10232 f.setValue(v.value);
10233 if(this.trackResetOnLoad){
10234 f.originalValue = f.getValue();
10238 }else{ // object hash
10241 if(typeof values[id] != 'function' && (field = this.findField(id))){
10243 if (field.setFromData &&
10244 field.valueField &&
10245 field.displayField &&
10246 // combos' with local stores can
10247 // be queried via setValue()
10248 // to set their value..
10249 (field.store && !field.store.isLocal)
10253 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10254 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10255 field.setFromData(sd);
10257 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10259 field.setFromData(values);
10262 field.setValue(values[id]);
10266 if(this.trackResetOnLoad){
10267 field.originalValue = field.getValue();
10273 //Roo.each(this.childForms || [], function (f) {
10274 // f.setValues(values);
10281 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10282 * they are returned as an array.
10283 * @param {Boolean} asString
10286 getValues : function(asString){
10287 //if (this.childForms) {
10288 // copy values from the child forms
10289 // Roo.each(this.childForms, function (f) {
10290 // this.setValues(f.getValues());
10296 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10297 if(asString === true){
10300 return Roo.urlDecode(fs);
10304 * Returns the fields in this form as an object with key/value pairs.
10305 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10308 getFieldValues : function(with_hidden)
10310 var items = this.getItems();
10312 items.each(function(f){
10314 if (!f.getName()) {
10318 var v = f.getValue();
10320 if (f.inputType =='radio') {
10321 if (typeof(ret[f.getName()]) == 'undefined') {
10322 ret[f.getName()] = ''; // empty..
10325 if (!f.el.dom.checked) {
10329 v = f.el.dom.value;
10333 if(f.xtype == 'MoneyField'){
10334 ret[f.currencyName] = f.getCurrency();
10337 // not sure if this supported any more..
10338 if ((typeof(v) == 'object') && f.getRawValue) {
10339 v = f.getRawValue() ; // dates..
10341 // combo boxes where name != hiddenName...
10342 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10343 ret[f.name] = f.getRawValue();
10345 ret[f.getName()] = v;
10352 * Clears all invalid messages in this form.
10353 * @return {BasicForm} this
10355 clearInvalid : function(){
10356 var items = this.getItems();
10358 items.each(function(f){
10366 * Resets this form.
10367 * @return {BasicForm} this
10369 reset : function(){
10370 var items = this.getItems();
10371 items.each(function(f){
10375 Roo.each(this.childForms || [], function (f) {
10383 getItems : function()
10385 var r=new Roo.util.MixedCollection(false, function(o){
10386 return o.id || (o.id = Roo.id());
10388 var iter = function(el) {
10395 Roo.each(el.items,function(e) {
10404 hideFields : function(items)
10406 Roo.each(items, function(i){
10408 var f = this.findField(i);
10419 showFields : function(items)
10421 Roo.each(items, function(i){
10423 var f = this.findField(i);
10436 Roo.apply(Roo.bootstrap.Form, {
10452 intervalID : false,
10458 if(this.isApplied){
10463 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10464 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10465 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10466 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10469 this.maskEl.top.enableDisplayMode("block");
10470 this.maskEl.left.enableDisplayMode("block");
10471 this.maskEl.bottom.enableDisplayMode("block");
10472 this.maskEl.right.enableDisplayMode("block");
10474 this.toolTip = new Roo.bootstrap.Tooltip({
10475 cls : 'roo-form-error-popover',
10477 'left' : ['r-l', [-2,0], 'right'],
10478 'right' : ['l-r', [2,0], 'left'],
10479 'bottom' : ['tl-bl', [0,2], 'top'],
10480 'top' : [ 'bl-tl', [0,-2], 'bottom']
10484 this.toolTip.render(Roo.get(document.body));
10486 this.toolTip.el.enableDisplayMode("block");
10488 Roo.get(document.body).on('click', function(){
10492 Roo.get(document.body).on('touchstart', function(){
10496 this.isApplied = true
10499 mask : function(form, target)
10503 this.target = target;
10505 if(!this.form.errorMask || !target.el){
10509 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10511 Roo.log(scrollable);
10513 var ot = this.target.el.calcOffsetsTo(scrollable);
10515 var scrollTo = ot[1] - this.form.maskOffset;
10517 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10519 scrollable.scrollTo('top', scrollTo);
10521 var box = this.target.el.getBox();
10523 var zIndex = Roo.bootstrap.Modal.zIndex++;
10526 this.maskEl.top.setStyle('position', 'absolute');
10527 this.maskEl.top.setStyle('z-index', zIndex);
10528 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10529 this.maskEl.top.setLeft(0);
10530 this.maskEl.top.setTop(0);
10531 this.maskEl.top.show();
10533 this.maskEl.left.setStyle('position', 'absolute');
10534 this.maskEl.left.setStyle('z-index', zIndex);
10535 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10536 this.maskEl.left.setLeft(0);
10537 this.maskEl.left.setTop(box.y - this.padding);
10538 this.maskEl.left.show();
10540 this.maskEl.bottom.setStyle('position', 'absolute');
10541 this.maskEl.bottom.setStyle('z-index', zIndex);
10542 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10543 this.maskEl.bottom.setLeft(0);
10544 this.maskEl.bottom.setTop(box.bottom + this.padding);
10545 this.maskEl.bottom.show();
10547 this.maskEl.right.setStyle('position', 'absolute');
10548 this.maskEl.right.setStyle('z-index', zIndex);
10549 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10550 this.maskEl.right.setLeft(box.right + this.padding);
10551 this.maskEl.right.setTop(box.y - this.padding);
10552 this.maskEl.right.show();
10554 this.toolTip.bindEl = this.target.el;
10556 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10558 var tip = this.target.blankText;
10560 if(this.target.getValue() !== '' ) {
10562 if (this.target.invalidText.length) {
10563 tip = this.target.invalidText;
10564 } else if (this.target.regexText.length){
10565 tip = this.target.regexText;
10569 this.toolTip.show(tip);
10571 this.intervalID = window.setInterval(function() {
10572 Roo.bootstrap.Form.popover.unmask();
10575 window.onwheel = function(){ return false;};
10577 (function(){ this.isMasked = true; }).defer(500, this);
10581 unmask : function()
10583 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10587 this.maskEl.top.setStyle('position', 'absolute');
10588 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10589 this.maskEl.top.hide();
10591 this.maskEl.left.setStyle('position', 'absolute');
10592 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10593 this.maskEl.left.hide();
10595 this.maskEl.bottom.setStyle('position', 'absolute');
10596 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10597 this.maskEl.bottom.hide();
10599 this.maskEl.right.setStyle('position', 'absolute');
10600 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10601 this.maskEl.right.hide();
10603 this.toolTip.hide();
10605 this.toolTip.el.hide();
10607 window.onwheel = function(){ return true;};
10609 if(this.intervalID){
10610 window.clearInterval(this.intervalID);
10611 this.intervalID = false;
10614 this.isMasked = false;
10624 * Ext JS Library 1.1.1
10625 * Copyright(c) 2006-2007, Ext JS, LLC.
10627 * Originally Released Under LGPL - original licence link has changed is not relivant.
10630 * <script type="text/javascript">
10633 * @class Roo.form.VTypes
10634 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10637 Roo.form.VTypes = function(){
10638 // closure these in so they are only created once.
10639 var alpha = /^[a-zA-Z_]+$/;
10640 var alphanum = /^[a-zA-Z0-9_]+$/;
10641 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10642 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10644 // All these messages and functions are configurable
10647 * The function used to validate email addresses
10648 * @param {String} value The email address
10650 'email' : function(v){
10651 return email.test(v);
10654 * The error text to display when the email validation function returns false
10657 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10659 * The keystroke filter mask to be applied on email input
10662 'emailMask' : /[a-z0-9_\.\-@]/i,
10665 * The function used to validate URLs
10666 * @param {String} value The URL
10668 'url' : function(v){
10669 return url.test(v);
10672 * The error text to display when the url validation function returns false
10675 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10678 * The function used to validate alpha values
10679 * @param {String} value The value
10681 'alpha' : function(v){
10682 return alpha.test(v);
10685 * The error text to display when the alpha validation function returns false
10688 'alphaText' : 'This field should only contain letters and _',
10690 * The keystroke filter mask to be applied on alpha input
10693 'alphaMask' : /[a-z_]/i,
10696 * The function used to validate alphanumeric values
10697 * @param {String} value The value
10699 'alphanum' : function(v){
10700 return alphanum.test(v);
10703 * The error text to display when the alphanumeric validation function returns false
10706 'alphanumText' : 'This field should only contain letters, numbers and _',
10708 * The keystroke filter mask to be applied on alphanumeric input
10711 'alphanumMask' : /[a-z0-9_]/i
10721 * @class Roo.bootstrap.Input
10722 * @extends Roo.bootstrap.Component
10723 * Bootstrap Input class
10724 * @cfg {Boolean} disabled is it disabled
10725 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10726 * @cfg {String} name name of the input
10727 * @cfg {string} fieldLabel - the label associated
10728 * @cfg {string} placeholder - placeholder to put in text.
10729 * @cfg {string} before - input group add on before
10730 * @cfg {string} after - input group add on after
10731 * @cfg {string} size - (lg|sm) or leave empty..
10732 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10733 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10734 * @cfg {Number} md colspan out of 12 for computer-sized screens
10735 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10736 * @cfg {string} value default value of the input
10737 * @cfg {Number} labelWidth set the width of label
10738 * @cfg {Number} labellg set the width of label (1-12)
10739 * @cfg {Number} labelmd set the width of label (1-12)
10740 * @cfg {Number} labelsm set the width of label (1-12)
10741 * @cfg {Number} labelxs set the width of label (1-12)
10742 * @cfg {String} labelAlign (top|left)
10743 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10744 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10745 * @cfg {String} indicatorpos (left|right) default left
10746 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10747 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10748 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10750 * @cfg {String} align (left|center|right) Default left
10751 * @cfg {Boolean} forceFeedback (true|false) Default false
10754 * Create a new Input
10755 * @param {Object} config The config object
10758 Roo.bootstrap.Input = function(config){
10760 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10765 * Fires when this field receives input focus.
10766 * @param {Roo.form.Field} this
10771 * Fires when this field loses input focus.
10772 * @param {Roo.form.Field} this
10776 * @event specialkey
10777 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10778 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10779 * @param {Roo.form.Field} this
10780 * @param {Roo.EventObject} e The event object
10785 * Fires just before the field blurs if the field value has changed.
10786 * @param {Roo.form.Field} this
10787 * @param {Mixed} newValue The new value
10788 * @param {Mixed} oldValue The original value
10793 * Fires after the field has been marked as invalid.
10794 * @param {Roo.form.Field} this
10795 * @param {String} msg The validation message
10800 * Fires after the field has been validated with no errors.
10801 * @param {Roo.form.Field} this
10806 * Fires after the key up
10807 * @param {Roo.form.Field} this
10808 * @param {Roo.EventObject} e The event Object
10813 * Fires after the user pastes into input
10814 * @param {Roo.form.Field} this
10815 * @param {Roo.EventObject} e The event Object
10821 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10823 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10824 automatic validation (defaults to "keyup").
10826 validationEvent : "keyup",
10828 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10830 validateOnBlur : true,
10832 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10834 validationDelay : 250,
10836 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10838 focusClass : "x-form-focus", // not needed???
10842 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10844 invalidClass : "has-warning",
10847 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10849 validClass : "has-success",
10852 * @cfg {Boolean} hasFeedback (true|false) default true
10854 hasFeedback : true,
10857 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10859 invalidFeedbackClass : "glyphicon-warning-sign",
10862 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10864 validFeedbackClass : "glyphicon-ok",
10867 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10869 selectOnFocus : false,
10872 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10876 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10881 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10883 disableKeyFilter : false,
10886 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10890 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10894 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10896 blankText : "Please complete this mandatory field",
10899 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10903 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10905 maxLength : Number.MAX_VALUE,
10907 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10909 minLengthText : "The minimum length for this field is {0}",
10911 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10913 maxLengthText : "The maximum length for this field is {0}",
10917 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10918 * If available, this function will be called only after the basic validators all return true, and will be passed the
10919 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10923 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10924 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10925 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10929 * @cfg {String} regexText -- Depricated - use Invalid Text
10934 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10940 autocomplete: false,
10944 inputType : 'text',
10947 placeholder: false,
10952 preventMark: false,
10953 isFormField : true,
10956 labelAlign : false,
10959 formatedValue : false,
10960 forceFeedback : false,
10962 indicatorpos : 'left',
10972 parentLabelAlign : function()
10975 while (parent.parent()) {
10976 parent = parent.parent();
10977 if (typeof(parent.labelAlign) !='undefined') {
10978 return parent.labelAlign;
10985 getAutoCreate : function()
10987 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10993 if(this.inputType != 'hidden'){
10994 cfg.cls = 'form-group' //input-group
11000 type : this.inputType,
11001 value : this.value,
11002 cls : 'form-control',
11003 placeholder : this.placeholder || '',
11004 autocomplete : this.autocomplete || 'new-password'
11006 if (this.inputType == 'file') {
11007 input.style = 'overflow:hidden'; // why not in CSS?
11010 if(this.capture.length){
11011 input.capture = this.capture;
11014 if(this.accept.length){
11015 input.accept = this.accept + "/*";
11019 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11022 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11023 input.maxLength = this.maxLength;
11026 if (this.disabled) {
11027 input.disabled=true;
11030 if (this.readOnly) {
11031 input.readonly=true;
11035 input.name = this.name;
11039 input.cls += ' input-' + this.size;
11043 ['xs','sm','md','lg'].map(function(size){
11044 if (settings[size]) {
11045 cfg.cls += ' col-' + size + '-' + settings[size];
11049 var inputblock = input;
11053 cls: 'glyphicon form-control-feedback'
11056 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11059 cls : 'has-feedback',
11067 if (this.before || this.after) {
11070 cls : 'input-group',
11074 if (this.before && typeof(this.before) == 'string') {
11076 inputblock.cn.push({
11078 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11082 if (this.before && typeof(this.before) == 'object') {
11083 this.before = Roo.factory(this.before);
11085 inputblock.cn.push({
11087 cls : 'roo-input-before input-group-prepend input-group-' +
11088 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11092 inputblock.cn.push(input);
11094 if (this.after && typeof(this.after) == 'string') {
11095 inputblock.cn.push({
11097 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11101 if (this.after && typeof(this.after) == 'object') {
11102 this.after = Roo.factory(this.after);
11104 inputblock.cn.push({
11106 cls : 'roo-input-after input-group-append input-group-' +
11107 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11111 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11112 inputblock.cls += ' has-feedback';
11113 inputblock.cn.push(feedback);
11118 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11119 tooltip : 'This field is required'
11121 if (this.allowBlank ) {
11122 indicator.style = this.allowBlank ? ' display:none' : '';
11124 if (align ==='left' && this.fieldLabel.length) {
11126 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11133 cls : 'control-label col-form-label',
11134 html : this.fieldLabel
11145 var labelCfg = cfg.cn[1];
11146 var contentCfg = cfg.cn[2];
11148 if(this.indicatorpos == 'right'){
11153 cls : 'control-label col-form-label',
11157 html : this.fieldLabel
11171 labelCfg = cfg.cn[0];
11172 contentCfg = cfg.cn[1];
11176 if(this.labelWidth > 12){
11177 labelCfg.style = "width: " + this.labelWidth + 'px';
11180 if(this.labelWidth < 13 && this.labelmd == 0){
11181 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11184 if(this.labellg > 0){
11185 labelCfg.cls += ' col-lg-' + this.labellg;
11186 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11189 if(this.labelmd > 0){
11190 labelCfg.cls += ' col-md-' + this.labelmd;
11191 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11194 if(this.labelsm > 0){
11195 labelCfg.cls += ' col-sm-' + this.labelsm;
11196 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11199 if(this.labelxs > 0){
11200 labelCfg.cls += ' col-xs-' + this.labelxs;
11201 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11205 } else if ( this.fieldLabel.length) {
11212 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11213 tooltip : 'This field is required',
11214 style : this.allowBlank ? ' display:none' : ''
11218 //cls : 'input-group-addon',
11219 html : this.fieldLabel
11227 if(this.indicatorpos == 'right'){
11232 //cls : 'input-group-addon',
11233 html : this.fieldLabel
11238 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11239 tooltip : 'This field is required',
11240 style : this.allowBlank ? ' display:none' : ''
11260 if (this.parentType === 'Navbar' && this.parent().bar) {
11261 cfg.cls += ' navbar-form';
11264 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11265 // on BS4 we do this only if not form
11266 cfg.cls += ' navbar-form';
11274 * return the real input element.
11276 inputEl: function ()
11278 return this.el.select('input.form-control',true).first();
11281 tooltipEl : function()
11283 return this.inputEl();
11286 indicatorEl : function()
11288 if (Roo.bootstrap.version == 4) {
11289 return false; // not enabled in v4 yet.
11292 var indicator = this.el.select('i.roo-required-indicator',true).first();
11302 setDisabled : function(v)
11304 var i = this.inputEl().dom;
11306 i.removeAttribute('disabled');
11310 i.setAttribute('disabled','true');
11312 initEvents : function()
11315 this.inputEl().on("keydown" , this.fireKey, this);
11316 this.inputEl().on("focus", this.onFocus, this);
11317 this.inputEl().on("blur", this.onBlur, this);
11319 this.inputEl().relayEvent('keyup', this);
11320 this.inputEl().relayEvent('paste', this);
11322 this.indicator = this.indicatorEl();
11324 if(this.indicator){
11325 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11328 // reference to original value for reset
11329 this.originalValue = this.getValue();
11330 //Roo.form.TextField.superclass.initEvents.call(this);
11331 if(this.validationEvent == 'keyup'){
11332 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11333 this.inputEl().on('keyup', this.filterValidation, this);
11335 else if(this.validationEvent !== false){
11336 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11339 if(this.selectOnFocus){
11340 this.on("focus", this.preFocus, this);
11343 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11344 this.inputEl().on("keypress", this.filterKeys, this);
11346 this.inputEl().relayEvent('keypress', this);
11349 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11350 this.el.on("click", this.autoSize, this);
11353 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11354 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11357 if (typeof(this.before) == 'object') {
11358 this.before.render(this.el.select('.roo-input-before',true).first());
11360 if (typeof(this.after) == 'object') {
11361 this.after.render(this.el.select('.roo-input-after',true).first());
11364 this.inputEl().on('change', this.onChange, this);
11367 filterValidation : function(e){
11368 if(!e.isNavKeyPress()){
11369 this.validationTask.delay(this.validationDelay);
11373 * Validates the field value
11374 * @return {Boolean} True if the value is valid, else false
11376 validate : function(){
11377 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11378 if(this.disabled || this.validateValue(this.getRawValue())){
11383 this.markInvalid();
11389 * Validates a value according to the field's validation rules and marks the field as invalid
11390 * if the validation fails
11391 * @param {Mixed} value The value to validate
11392 * @return {Boolean} True if the value is valid, else false
11394 validateValue : function(value)
11396 if(this.getVisibilityEl().hasClass('hidden')){
11400 if(value.length < 1) { // if it's blank
11401 if(this.allowBlank){
11407 if(value.length < this.minLength){
11410 if(value.length > this.maxLength){
11414 var vt = Roo.form.VTypes;
11415 if(!vt[this.vtype](value, this)){
11419 if(typeof this.validator == "function"){
11420 var msg = this.validator(value);
11424 if (typeof(msg) == 'string') {
11425 this.invalidText = msg;
11429 if(this.regex && !this.regex.test(value)){
11437 fireKey : function(e){
11438 //Roo.log('field ' + e.getKey());
11439 if(e.isNavKeyPress()){
11440 this.fireEvent("specialkey", this, e);
11443 focus : function (selectText){
11445 this.inputEl().focus();
11446 if(selectText === true){
11447 this.inputEl().dom.select();
11453 onFocus : function(){
11454 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11455 // this.el.addClass(this.focusClass);
11457 if(!this.hasFocus){
11458 this.hasFocus = true;
11459 this.startValue = this.getValue();
11460 this.fireEvent("focus", this);
11464 beforeBlur : Roo.emptyFn,
11468 onBlur : function(){
11470 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11471 //this.el.removeClass(this.focusClass);
11473 this.hasFocus = false;
11474 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11477 var v = this.getValue();
11478 if(String(v) !== String(this.startValue)){
11479 this.fireEvent('change', this, v, this.startValue);
11481 this.fireEvent("blur", this);
11484 onChange : function(e)
11486 var v = this.getValue();
11487 if(String(v) !== String(this.startValue)){
11488 this.fireEvent('change', this, v, this.startValue);
11494 * Resets the current field value to the originally loaded value and clears any validation messages
11496 reset : function(){
11497 this.setValue(this.originalValue);
11501 * Returns the name of the field
11502 * @return {Mixed} name The name field
11504 getName: function(){
11508 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11509 * @return {Mixed} value The field value
11511 getValue : function(){
11513 var v = this.inputEl().getValue();
11518 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11519 * @return {Mixed} value The field value
11521 getRawValue : function(){
11522 var v = this.inputEl().getValue();
11528 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11529 * @param {Mixed} value The value to set
11531 setRawValue : function(v){
11532 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11535 selectText : function(start, end){
11536 var v = this.getRawValue();
11538 start = start === undefined ? 0 : start;
11539 end = end === undefined ? v.length : end;
11540 var d = this.inputEl().dom;
11541 if(d.setSelectionRange){
11542 d.setSelectionRange(start, end);
11543 }else if(d.createTextRange){
11544 var range = d.createTextRange();
11545 range.moveStart("character", start);
11546 range.moveEnd("character", v.length-end);
11553 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11554 * @param {Mixed} value The value to set
11556 setValue : function(v){
11559 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11565 processValue : function(value){
11566 if(this.stripCharsRe){
11567 var newValue = value.replace(this.stripCharsRe, '');
11568 if(newValue !== value){
11569 this.setRawValue(newValue);
11576 preFocus : function(){
11578 if(this.selectOnFocus){
11579 this.inputEl().dom.select();
11582 filterKeys : function(e){
11583 var k = e.getKey();
11584 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11587 var c = e.getCharCode(), cc = String.fromCharCode(c);
11588 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11591 if(!this.maskRe.test(cc)){
11596 * Clear any invalid styles/messages for this field
11598 clearInvalid : function(){
11600 if(!this.el || this.preventMark){ // not rendered
11605 this.el.removeClass([this.invalidClass, 'is-invalid']);
11607 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11609 var feedback = this.el.select('.form-control-feedback', true).first();
11612 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11617 if(this.indicator){
11618 this.indicator.removeClass('visible');
11619 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11622 this.fireEvent('valid', this);
11626 * Mark this field as valid
11628 markValid : function()
11630 if(!this.el || this.preventMark){ // not rendered...
11634 this.el.removeClass([this.invalidClass, this.validClass]);
11635 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11637 var feedback = this.el.select('.form-control-feedback', true).first();
11640 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11643 if(this.indicator){
11644 this.indicator.removeClass('visible');
11645 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11653 if(this.allowBlank && !this.getRawValue().length){
11656 if (Roo.bootstrap.version == 3) {
11657 this.el.addClass(this.validClass);
11659 this.inputEl().addClass('is-valid');
11662 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11664 var feedback = this.el.select('.form-control-feedback', true).first();
11667 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11668 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11673 this.fireEvent('valid', this);
11677 * Mark this field as invalid
11678 * @param {String} msg The validation message
11680 markInvalid : function(msg)
11682 if(!this.el || this.preventMark){ // not rendered
11686 this.el.removeClass([this.invalidClass, this.validClass]);
11687 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11689 var feedback = this.el.select('.form-control-feedback', true).first();
11692 this.el.select('.form-control-feedback', true).first().removeClass(
11693 [this.invalidFeedbackClass, this.validFeedbackClass]);
11700 if(this.allowBlank && !this.getRawValue().length){
11704 if(this.indicator){
11705 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11706 this.indicator.addClass('visible');
11708 if (Roo.bootstrap.version == 3) {
11709 this.el.addClass(this.invalidClass);
11711 this.inputEl().addClass('is-invalid');
11716 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11718 var feedback = this.el.select('.form-control-feedback', true).first();
11721 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11723 if(this.getValue().length || this.forceFeedback){
11724 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11731 this.fireEvent('invalid', this, msg);
11734 SafariOnKeyDown : function(event)
11736 // this is a workaround for a password hang bug on chrome/ webkit.
11737 if (this.inputEl().dom.type != 'password') {
11741 var isSelectAll = false;
11743 if(this.inputEl().dom.selectionEnd > 0){
11744 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11746 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11747 event.preventDefault();
11752 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11754 event.preventDefault();
11755 // this is very hacky as keydown always get's upper case.
11757 var cc = String.fromCharCode(event.getCharCode());
11758 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11762 adjustWidth : function(tag, w){
11763 tag = tag.toLowerCase();
11764 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11765 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11766 if(tag == 'input'){
11769 if(tag == 'textarea'){
11772 }else if(Roo.isOpera){
11773 if(tag == 'input'){
11776 if(tag == 'textarea'){
11784 setFieldLabel : function(v)
11786 if(!this.rendered){
11790 if(this.indicatorEl()){
11791 var ar = this.el.select('label > span',true);
11793 if (ar.elements.length) {
11794 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11795 this.fieldLabel = v;
11799 var br = this.el.select('label',true);
11801 if(br.elements.length) {
11802 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11803 this.fieldLabel = v;
11807 Roo.log('Cannot Found any of label > span || label in input');
11811 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11812 this.fieldLabel = v;
11827 * @class Roo.bootstrap.TextArea
11828 * @extends Roo.bootstrap.Input
11829 * Bootstrap TextArea class
11830 * @cfg {Number} cols Specifies the visible width of a text area
11831 * @cfg {Number} rows Specifies the visible number of lines in a text area
11832 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11833 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11834 * @cfg {string} html text
11837 * Create a new TextArea
11838 * @param {Object} config The config object
11841 Roo.bootstrap.TextArea = function(config){
11842 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11846 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11856 getAutoCreate : function(){
11858 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11864 if(this.inputType != 'hidden'){
11865 cfg.cls = 'form-group' //input-group
11873 value : this.value || '',
11874 html: this.html || '',
11875 cls : 'form-control',
11876 placeholder : this.placeholder || ''
11880 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11881 input.maxLength = this.maxLength;
11885 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11889 input.cols = this.cols;
11892 if (this.readOnly) {
11893 input.readonly = true;
11897 input.name = this.name;
11901 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11905 ['xs','sm','md','lg'].map(function(size){
11906 if (settings[size]) {
11907 cfg.cls += ' col-' + size + '-' + settings[size];
11911 var inputblock = input;
11913 if(this.hasFeedback && !this.allowBlank){
11917 cls: 'glyphicon form-control-feedback'
11921 cls : 'has-feedback',
11930 if (this.before || this.after) {
11933 cls : 'input-group',
11937 inputblock.cn.push({
11939 cls : 'input-group-addon',
11944 inputblock.cn.push(input);
11946 if(this.hasFeedback && !this.allowBlank){
11947 inputblock.cls += ' has-feedback';
11948 inputblock.cn.push(feedback);
11952 inputblock.cn.push({
11954 cls : 'input-group-addon',
11961 if (align ==='left' && this.fieldLabel.length) {
11966 cls : 'control-label',
11967 html : this.fieldLabel
11978 if(this.labelWidth > 12){
11979 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11982 if(this.labelWidth < 13 && this.labelmd == 0){
11983 this.labelmd = this.labelWidth;
11986 if(this.labellg > 0){
11987 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11988 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11991 if(this.labelmd > 0){
11992 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11993 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11996 if(this.labelsm > 0){
11997 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11998 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12001 if(this.labelxs > 0){
12002 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12003 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12006 } else if ( this.fieldLabel.length) {
12011 //cls : 'input-group-addon',
12012 html : this.fieldLabel
12030 if (this.disabled) {
12031 input.disabled=true;
12038 * return the real textarea element.
12040 inputEl: function ()
12042 return this.el.select('textarea.form-control',true).first();
12046 * Clear any invalid styles/messages for this field
12048 clearInvalid : function()
12051 if(!this.el || this.preventMark){ // not rendered
12055 var label = this.el.select('label', true).first();
12056 var icon = this.el.select('i.fa-star', true).first();
12061 this.el.removeClass( this.validClass);
12062 this.inputEl().removeClass('is-invalid');
12064 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12066 var feedback = this.el.select('.form-control-feedback', true).first();
12069 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12074 this.fireEvent('valid', this);
12078 * Mark this field as valid
12080 markValid : function()
12082 if(!this.el || this.preventMark){ // not rendered
12086 this.el.removeClass([this.invalidClass, this.validClass]);
12087 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12089 var feedback = this.el.select('.form-control-feedback', true).first();
12092 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12095 if(this.disabled || this.allowBlank){
12099 var label = this.el.select('label', true).first();
12100 var icon = this.el.select('i.fa-star', true).first();
12105 if (Roo.bootstrap.version == 3) {
12106 this.el.addClass(this.validClass);
12108 this.inputEl().addClass('is-valid');
12112 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12114 var feedback = this.el.select('.form-control-feedback', true).first();
12117 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12118 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12123 this.fireEvent('valid', this);
12127 * Mark this field as invalid
12128 * @param {String} msg The validation message
12130 markInvalid : function(msg)
12132 if(!this.el || this.preventMark){ // not rendered
12136 this.el.removeClass([this.invalidClass, this.validClass]);
12137 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12139 var feedback = this.el.select('.form-control-feedback', true).first();
12142 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12145 if(this.disabled || this.allowBlank){
12149 var label = this.el.select('label', true).first();
12150 var icon = this.el.select('i.fa-star', true).first();
12152 if(!this.getValue().length && label && !icon){
12153 this.el.createChild({
12155 cls : 'text-danger fa fa-lg fa-star',
12156 tooltip : 'This field is required',
12157 style : 'margin-right:5px;'
12161 if (Roo.bootstrap.version == 3) {
12162 this.el.addClass(this.invalidClass);
12164 this.inputEl().addClass('is-invalid');
12167 // fixme ... this may be depricated need to test..
12168 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12170 var feedback = this.el.select('.form-control-feedback', true).first();
12173 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12175 if(this.getValue().length || this.forceFeedback){
12176 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12183 this.fireEvent('invalid', this, msg);
12191 * trigger field - base class for combo..
12196 * @class Roo.bootstrap.TriggerField
12197 * @extends Roo.bootstrap.Input
12198 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12199 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12200 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12201 * for which you can provide a custom implementation. For example:
12203 var trigger = new Roo.bootstrap.TriggerField();
12204 trigger.onTriggerClick = myTriggerFn;
12205 trigger.applyTo('my-field');
12208 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12209 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12210 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12211 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12212 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12215 * Create a new TriggerField.
12216 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12217 * to the base TextField)
12219 Roo.bootstrap.TriggerField = function(config){
12220 this.mimicing = false;
12221 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12224 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12226 * @cfg {String} triggerClass A CSS class to apply to the trigger
12229 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12234 * @cfg {Boolean} removable (true|false) special filter default false
12238 /** @cfg {Boolean} grow @hide */
12239 /** @cfg {Number} growMin @hide */
12240 /** @cfg {Number} growMax @hide */
12246 autoSize: Roo.emptyFn,
12250 deferHeight : true,
12253 actionMode : 'wrap',
12258 getAutoCreate : function(){
12260 var align = this.labelAlign || this.parentLabelAlign();
12265 cls: 'form-group' //input-group
12272 type : this.inputType,
12273 cls : 'form-control',
12274 autocomplete: 'new-password',
12275 placeholder : this.placeholder || ''
12279 input.name = this.name;
12282 input.cls += ' input-' + this.size;
12285 if (this.disabled) {
12286 input.disabled=true;
12289 var inputblock = input;
12291 if(this.hasFeedback && !this.allowBlank){
12295 cls: 'glyphicon form-control-feedback'
12298 if(this.removable && !this.editable ){
12300 cls : 'has-feedback',
12306 cls : 'roo-combo-removable-btn close'
12313 cls : 'has-feedback',
12322 if(this.removable && !this.editable ){
12324 cls : 'roo-removable',
12330 cls : 'roo-combo-removable-btn close'
12337 if (this.before || this.after) {
12340 cls : 'input-group',
12344 inputblock.cn.push({
12346 cls : 'input-group-addon input-group-prepend input-group-text',
12351 inputblock.cn.push(input);
12353 if(this.hasFeedback && !this.allowBlank){
12354 inputblock.cls += ' has-feedback';
12355 inputblock.cn.push(feedback);
12359 inputblock.cn.push({
12361 cls : 'input-group-addon input-group-append input-group-text',
12370 var ibwrap = inputblock;
12375 cls: 'roo-select2-choices',
12379 cls: 'roo-select2-search-field',
12391 cls: 'roo-select2-container input-group',
12396 cls: 'form-hidden-field'
12402 if(!this.multiple && this.showToggleBtn){
12408 if (this.caret != false) {
12411 cls: 'fa fa-' + this.caret
12418 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12420 Roo.bootstrap.version == 3 ? caret : '',
12423 cls: 'combobox-clear',
12437 combobox.cls += ' roo-select2-container-multi';
12441 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12442 tooltip : 'This field is required'
12444 if (Roo.bootstrap.version == 4) {
12447 style : 'display:none'
12452 if (align ==='left' && this.fieldLabel.length) {
12454 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12461 cls : 'control-label',
12462 html : this.fieldLabel
12474 var labelCfg = cfg.cn[1];
12475 var contentCfg = cfg.cn[2];
12477 if(this.indicatorpos == 'right'){
12482 cls : 'control-label',
12486 html : this.fieldLabel
12500 labelCfg = cfg.cn[0];
12501 contentCfg = cfg.cn[1];
12504 if(this.labelWidth > 12){
12505 labelCfg.style = "width: " + this.labelWidth + 'px';
12508 if(this.labelWidth < 13 && this.labelmd == 0){
12509 this.labelmd = this.labelWidth;
12512 if(this.labellg > 0){
12513 labelCfg.cls += ' col-lg-' + this.labellg;
12514 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12517 if(this.labelmd > 0){
12518 labelCfg.cls += ' col-md-' + this.labelmd;
12519 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12522 if(this.labelsm > 0){
12523 labelCfg.cls += ' col-sm-' + this.labelsm;
12524 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12527 if(this.labelxs > 0){
12528 labelCfg.cls += ' col-xs-' + this.labelxs;
12529 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12532 } else if ( this.fieldLabel.length) {
12533 // Roo.log(" label");
12538 //cls : 'input-group-addon',
12539 html : this.fieldLabel
12547 if(this.indicatorpos == 'right'){
12555 html : this.fieldLabel
12569 // Roo.log(" no label && no align");
12576 ['xs','sm','md','lg'].map(function(size){
12577 if (settings[size]) {
12578 cfg.cls += ' col-' + size + '-' + settings[size];
12589 onResize : function(w, h){
12590 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12591 // if(typeof w == 'number'){
12592 // var x = w - this.trigger.getWidth();
12593 // this.inputEl().setWidth(this.adjustWidth('input', x));
12594 // this.trigger.setStyle('left', x+'px');
12599 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12602 getResizeEl : function(){
12603 return this.inputEl();
12607 getPositionEl : function(){
12608 return this.inputEl();
12612 alignErrorIcon : function(){
12613 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12617 initEvents : function(){
12621 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12622 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12623 if(!this.multiple && this.showToggleBtn){
12624 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12625 if(this.hideTrigger){
12626 this.trigger.setDisplayed(false);
12628 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12632 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12635 if(this.removable && !this.editable && !this.tickable){
12636 var close = this.closeTriggerEl();
12639 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12640 close.on('click', this.removeBtnClick, this, close);
12644 //this.trigger.addClassOnOver('x-form-trigger-over');
12645 //this.trigger.addClassOnClick('x-form-trigger-click');
12648 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12652 closeTriggerEl : function()
12654 var close = this.el.select('.roo-combo-removable-btn', true).first();
12655 return close ? close : false;
12658 removeBtnClick : function(e, h, el)
12660 e.preventDefault();
12662 if(this.fireEvent("remove", this) !== false){
12664 this.fireEvent("afterremove", this)
12668 createList : function()
12670 this.list = Roo.get(document.body).createChild({
12671 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12672 cls: 'typeahead typeahead-long dropdown-menu shadow',
12673 style: 'display:none'
12676 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12681 initTrigger : function(){
12686 onDestroy : function(){
12688 this.trigger.removeAllListeners();
12689 // this.trigger.remove();
12692 // this.wrap.remove();
12694 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12698 onFocus : function(){
12699 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12701 if(!this.mimicing){
12702 this.wrap.addClass('x-trigger-wrap-focus');
12703 this.mimicing = true;
12704 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12705 if(this.monitorTab){
12706 this.el.on("keydown", this.checkTab, this);
12713 checkTab : function(e){
12714 if(e.getKey() == e.TAB){
12715 this.triggerBlur();
12720 onBlur : function(){
12725 mimicBlur : function(e, t){
12727 if(!this.wrap.contains(t) && this.validateBlur()){
12728 this.triggerBlur();
12734 triggerBlur : function(){
12735 this.mimicing = false;
12736 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12737 if(this.monitorTab){
12738 this.el.un("keydown", this.checkTab, this);
12740 //this.wrap.removeClass('x-trigger-wrap-focus');
12741 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12745 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12746 validateBlur : function(e, t){
12751 onDisable : function(){
12752 this.inputEl().dom.disabled = true;
12753 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12755 // this.wrap.addClass('x-item-disabled');
12760 onEnable : function(){
12761 this.inputEl().dom.disabled = false;
12762 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12764 // this.el.removeClass('x-item-disabled');
12769 onShow : function(){
12770 var ae = this.getActionEl();
12773 ae.dom.style.display = '';
12774 ae.dom.style.visibility = 'visible';
12780 onHide : function(){
12781 var ae = this.getActionEl();
12782 ae.dom.style.display = 'none';
12786 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12787 * by an implementing function.
12789 * @param {EventObject} e
12791 onTriggerClick : Roo.emptyFn
12799 * @class Roo.bootstrap.CardUploader
12800 * @extends Roo.bootstrap.Button
12801 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12802 * @cfg {Number} errorTimeout default 3000
12803 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12804 * @cfg {Array} html The button text.
12808 * Create a new CardUploader
12809 * @param {Object} config The config object
12812 Roo.bootstrap.CardUploader = function(config){
12816 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12819 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12827 * When a image is clicked on - and needs to display a slideshow or similar..
12828 * @param {Roo.bootstrap.Card} this
12829 * @param {Object} The image information data
12835 * When a the download link is clicked
12836 * @param {Roo.bootstrap.Card} this
12837 * @param {Object} The image information data contains
12844 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12847 errorTimeout : 3000,
12851 fileCollection : false,
12854 getAutoCreate : function()
12858 cls :'form-group' ,
12863 //cls : 'input-group-addon',
12864 html : this.fieldLabel
12872 value : this.value,
12873 cls : 'd-none form-control'
12878 multiple : 'multiple',
12880 cls : 'd-none roo-card-upload-selector'
12884 cls : 'roo-card-uploader-button-container w-100 mb-2'
12887 cls : 'card-columns roo-card-uploader-container'
12897 getChildContainer : function() /// what children are added to.
12899 return this.containerEl;
12902 getButtonContainer : function() /// what children are added to.
12904 return this.el.select(".roo-card-uploader-button-container").first();
12907 initEvents : function()
12910 Roo.bootstrap.Input.prototype.initEvents.call(this);
12914 xns: Roo.bootstrap,
12917 container_method : 'getButtonContainer' ,
12918 html : this.html, // fix changable?
12921 'click' : function(btn, e) {
12930 this.urlAPI = (window.createObjectURL && window) ||
12931 (window.URL && URL.revokeObjectURL && URL) ||
12932 (window.webkitURL && webkitURL);
12937 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12939 this.selectorEl.on('change', this.onFileSelected, this);
12942 this.images.forEach(function(img) {
12945 this.images = false;
12947 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12953 onClick : function(e)
12955 e.preventDefault();
12957 this.selectorEl.dom.click();
12961 onFileSelected : function(e)
12963 e.preventDefault();
12965 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12969 Roo.each(this.selectorEl.dom.files, function(file){
12970 this.addFile(file);
12979 addFile : function(file)
12982 if(typeof(file) === 'string'){
12983 throw "Add file by name?"; // should not happen
12987 if(!file || !this.urlAPI){
12997 var url = _this.urlAPI.createObjectURL( file);
13000 id : Roo.bootstrap.CardUploader.ID--,
13001 is_uploaded : false,
13005 mimetype : file.type,
13013 * addCard - add an Attachment to the uploader
13014 * @param data - the data about the image to upload
13018 title : "Title of file",
13019 is_uploaded : false,
13020 src : "http://.....",
13021 srcfile : { the File upload object },
13022 mimetype : file.type,
13025 .. any other data...
13031 addCard : function (data)
13033 // hidden input element?
13034 // if the file is not an image...
13035 //then we need to use something other that and header_image
13040 xns : Roo.bootstrap,
13041 xtype : 'CardFooter',
13044 xns : Roo.bootstrap,
13050 xns : Roo.bootstrap,
13052 html : String.format("<small>{0}</small>", data.title),
13053 cls : 'col-10 text-left',
13058 click : function() {
13060 t.fireEvent( "download", t, data );
13066 xns : Roo.bootstrap,
13068 style: 'max-height: 28px; ',
13074 click : function() {
13075 t.removeCard(data.id)
13087 var cn = this.addxtype(
13090 xns : Roo.bootstrap,
13093 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13094 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13095 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13100 initEvents : function() {
13101 Roo.bootstrap.Card.prototype.initEvents.call(this);
13103 this.imgEl = this.el.select('.card-img-top').first();
13105 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13106 this.imgEl.set({ 'pointer' : 'cursor' });
13109 this.getCardFooter().addClass('p-1');
13116 // dont' really need ot update items.
13117 // this.items.push(cn);
13118 this.fileCollection.add(cn);
13120 if (!data.srcfile) {
13121 this.updateInput();
13126 var reader = new FileReader();
13127 reader.addEventListener("load", function() {
13128 data.srcdata = reader.result;
13131 reader.readAsDataURL(data.srcfile);
13136 removeCard : function(id)
13139 var card = this.fileCollection.get(id);
13140 card.data.is_deleted = 1;
13141 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13142 //this.fileCollection.remove(card);
13143 //this.items = this.items.filter(function(e) { return e != card });
13144 // dont' really need ot update items.
13145 card.el.dom.parentNode.removeChild(card.el.dom);
13146 this.updateInput();
13152 this.fileCollection.each(function(card) {
13153 if (card.el.dom && card.el.dom.parentNode) {
13154 card.el.dom.parentNode.removeChild(card.el.dom);
13157 this.fileCollection.clear();
13158 this.updateInput();
13161 updateInput : function()
13164 this.fileCollection.each(function(e) {
13168 this.inputEl().dom.value = JSON.stringify(data);
13178 Roo.bootstrap.CardUploader.ID = -1;/*
13180 * Ext JS Library 1.1.1
13181 * Copyright(c) 2006-2007, Ext JS, LLC.
13183 * Originally Released Under LGPL - original licence link has changed is not relivant.
13186 * <script type="text/javascript">
13191 * @class Roo.data.SortTypes
13193 * Defines the default sorting (casting?) comparison functions used when sorting data.
13195 Roo.data.SortTypes = {
13197 * Default sort that does nothing
13198 * @param {Mixed} s The value being converted
13199 * @return {Mixed} The comparison value
13201 none : function(s){
13206 * The regular expression used to strip tags
13210 stripTagsRE : /<\/?[^>]+>/gi,
13213 * Strips all HTML tags to sort on text only
13214 * @param {Mixed} s The value being converted
13215 * @return {String} The comparison value
13217 asText : function(s){
13218 return String(s).replace(this.stripTagsRE, "");
13222 * Strips all HTML tags to sort on text only - Case insensitive
13223 * @param {Mixed} s The value being converted
13224 * @return {String} The comparison value
13226 asUCText : function(s){
13227 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13231 * Case insensitive string
13232 * @param {Mixed} s The value being converted
13233 * @return {String} The comparison value
13235 asUCString : function(s) {
13236 return String(s).toUpperCase();
13241 * @param {Mixed} s The value being converted
13242 * @return {Number} The comparison value
13244 asDate : function(s) {
13248 if(s instanceof Date){
13249 return s.getTime();
13251 return Date.parse(String(s));
13256 * @param {Mixed} s The value being converted
13257 * @return {Float} The comparison value
13259 asFloat : function(s) {
13260 var val = parseFloat(String(s).replace(/,/g, ""));
13269 * @param {Mixed} s The value being converted
13270 * @return {Number} The comparison value
13272 asInt : function(s) {
13273 var val = parseInt(String(s).replace(/,/g, ""));
13281 * Ext JS Library 1.1.1
13282 * Copyright(c) 2006-2007, Ext JS, LLC.
13284 * Originally Released Under LGPL - original licence link has changed is not relivant.
13287 * <script type="text/javascript">
13291 * @class Roo.data.Record
13292 * Instances of this class encapsulate both record <em>definition</em> information, and record
13293 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13294 * to access Records cached in an {@link Roo.data.Store} object.<br>
13296 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13297 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13300 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13302 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13303 * {@link #create}. The parameters are the same.
13304 * @param {Array} data An associative Array of data values keyed by the field name.
13305 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13306 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13307 * not specified an integer id is generated.
13309 Roo.data.Record = function(data, id){
13310 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13315 * Generate a constructor for a specific record layout.
13316 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13317 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13318 * Each field definition object may contain the following properties: <ul>
13319 * <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,
13320 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13321 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13322 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13323 * is being used, then this is a string containing the javascript expression to reference the data relative to
13324 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13325 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13326 * this may be omitted.</p></li>
13327 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13328 * <ul><li>auto (Default, implies no conversion)</li>
13333 * <li>date</li></ul></p></li>
13334 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13335 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13336 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13337 * by the Reader into an object that will be stored in the Record. It is passed the
13338 * following parameters:<ul>
13339 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13341 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13343 * <br>usage:<br><pre><code>
13344 var TopicRecord = Roo.data.Record.create(
13345 {name: 'title', mapping: 'topic_title'},
13346 {name: 'author', mapping: 'username'},
13347 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13348 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13349 {name: 'lastPoster', mapping: 'user2'},
13350 {name: 'excerpt', mapping: 'post_text'}
13353 var myNewRecord = new TopicRecord({
13354 title: 'Do my job please',
13357 lastPost: new Date(),
13358 lastPoster: 'Animal',
13359 excerpt: 'No way dude!'
13361 myStore.add(myNewRecord);
13366 Roo.data.Record.create = function(o){
13367 var f = function(){
13368 f.superclass.constructor.apply(this, arguments);
13370 Roo.extend(f, Roo.data.Record);
13371 var p = f.prototype;
13372 p.fields = new Roo.util.MixedCollection(false, function(field){
13375 for(var i = 0, len = o.length; i < len; i++){
13376 p.fields.add(new Roo.data.Field(o[i]));
13378 f.getField = function(name){
13379 return p.fields.get(name);
13384 Roo.data.Record.AUTO_ID = 1000;
13385 Roo.data.Record.EDIT = 'edit';
13386 Roo.data.Record.REJECT = 'reject';
13387 Roo.data.Record.COMMIT = 'commit';
13389 Roo.data.Record.prototype = {
13391 * Readonly flag - true if this record has been modified.
13400 join : function(store){
13401 this.store = store;
13405 * Set the named field to the specified value.
13406 * @param {String} name The name of the field to set.
13407 * @param {Object} value The value to set the field to.
13409 set : function(name, value){
13410 if(this.data[name] == value){
13414 if(!this.modified){
13415 this.modified = {};
13417 if(typeof this.modified[name] == 'undefined'){
13418 this.modified[name] = this.data[name];
13420 this.data[name] = value;
13421 if(!this.editing && this.store){
13422 this.store.afterEdit(this);
13427 * Get the value of the named field.
13428 * @param {String} name The name of the field to get the value of.
13429 * @return {Object} The value of the field.
13431 get : function(name){
13432 return this.data[name];
13436 beginEdit : function(){
13437 this.editing = true;
13438 this.modified = {};
13442 cancelEdit : function(){
13443 this.editing = false;
13444 delete this.modified;
13448 endEdit : function(){
13449 this.editing = false;
13450 if(this.dirty && this.store){
13451 this.store.afterEdit(this);
13456 * Usually called by the {@link Roo.data.Store} which owns the Record.
13457 * Rejects all changes made to the Record since either creation, or the last commit operation.
13458 * Modified fields are reverted to their original values.
13460 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13461 * of reject operations.
13463 reject : function(){
13464 var m = this.modified;
13466 if(typeof m[n] != "function"){
13467 this.data[n] = m[n];
13470 this.dirty = false;
13471 delete this.modified;
13472 this.editing = false;
13474 this.store.afterReject(this);
13479 * Usually called by the {@link Roo.data.Store} which owns the Record.
13480 * Commits all changes made to the Record since either creation, or the last commit operation.
13482 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13483 * of commit operations.
13485 commit : function(){
13486 this.dirty = false;
13487 delete this.modified;
13488 this.editing = false;
13490 this.store.afterCommit(this);
13495 hasError : function(){
13496 return this.error != null;
13500 clearError : function(){
13505 * Creates a copy of this record.
13506 * @param {String} id (optional) A new record id if you don't want to use this record's id
13509 copy : function(newId) {
13510 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13514 * Ext JS Library 1.1.1
13515 * Copyright(c) 2006-2007, Ext JS, LLC.
13517 * Originally Released Under LGPL - original licence link has changed is not relivant.
13520 * <script type="text/javascript">
13526 * @class Roo.data.Store
13527 * @extends Roo.util.Observable
13528 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13529 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13531 * 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
13532 * has no knowledge of the format of the data returned by the Proxy.<br>
13534 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13535 * instances from the data object. These records are cached and made available through accessor functions.
13537 * Creates a new Store.
13538 * @param {Object} config A config object containing the objects needed for the Store to access data,
13539 * and read the data into Records.
13541 Roo.data.Store = function(config){
13542 this.data = new Roo.util.MixedCollection(false);
13543 this.data.getKey = function(o){
13546 this.baseParams = {};
13548 this.paramNames = {
13553 "multisort" : "_multisort"
13556 if(config && config.data){
13557 this.inlineData = config.data;
13558 delete config.data;
13561 Roo.apply(this, config);
13563 if(this.reader){ // reader passed
13564 this.reader = Roo.factory(this.reader, Roo.data);
13565 this.reader.xmodule = this.xmodule || false;
13566 if(!this.recordType){
13567 this.recordType = this.reader.recordType;
13569 if(this.reader.onMetaChange){
13570 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13574 if(this.recordType){
13575 this.fields = this.recordType.prototype.fields;
13577 this.modified = [];
13581 * @event datachanged
13582 * Fires when the data cache has changed, and a widget which is using this Store
13583 * as a Record cache should refresh its view.
13584 * @param {Store} this
13586 datachanged : true,
13588 * @event metachange
13589 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13590 * @param {Store} this
13591 * @param {Object} meta The JSON metadata
13596 * Fires when Records have been added to the Store
13597 * @param {Store} this
13598 * @param {Roo.data.Record[]} records The array of Records added
13599 * @param {Number} index The index at which the record(s) were added
13604 * Fires when a Record has been removed from the Store
13605 * @param {Store} this
13606 * @param {Roo.data.Record} record The Record that was removed
13607 * @param {Number} index The index at which the record was removed
13612 * Fires when a Record has been updated
13613 * @param {Store} this
13614 * @param {Roo.data.Record} record The Record that was updated
13615 * @param {String} operation The update operation being performed. Value may be one of:
13617 Roo.data.Record.EDIT
13618 Roo.data.Record.REJECT
13619 Roo.data.Record.COMMIT
13625 * Fires when the data cache has been cleared.
13626 * @param {Store} this
13630 * @event beforeload
13631 * Fires before a request is made for a new data object. If the beforeload handler returns false
13632 * the load action will be canceled.
13633 * @param {Store} this
13634 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13638 * @event beforeloadadd
13639 * Fires after a new set of Records has been loaded.
13640 * @param {Store} this
13641 * @param {Roo.data.Record[]} records The Records that were loaded
13642 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13644 beforeloadadd : true,
13647 * Fires after a new set of Records has been loaded, before they are added to the store.
13648 * @param {Store} this
13649 * @param {Roo.data.Record[]} records The Records that were loaded
13650 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13651 * @params {Object} return from reader
13655 * @event loadexception
13656 * Fires if an exception occurs in the Proxy during loading.
13657 * Called with the signature of the Proxy's "loadexception" event.
13658 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13661 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13662 * @param {Object} load options
13663 * @param {Object} jsonData from your request (normally this contains the Exception)
13665 loadexception : true
13669 this.proxy = Roo.factory(this.proxy, Roo.data);
13670 this.proxy.xmodule = this.xmodule || false;
13671 this.relayEvents(this.proxy, ["loadexception"]);
13673 this.sortToggle = {};
13674 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13676 Roo.data.Store.superclass.constructor.call(this);
13678 if(this.inlineData){
13679 this.loadData(this.inlineData);
13680 delete this.inlineData;
13684 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13686 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13687 * without a remote query - used by combo/forms at present.
13691 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13694 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13697 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13698 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13701 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13702 * on any HTTP request
13705 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13708 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13712 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13713 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13715 remoteSort : false,
13718 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13719 * loaded or when a record is removed. (defaults to false).
13721 pruneModifiedRecords : false,
13724 lastOptions : null,
13727 * Add Records to the Store and fires the add event.
13728 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13730 add : function(records){
13731 records = [].concat(records);
13732 for(var i = 0, len = records.length; i < len; i++){
13733 records[i].join(this);
13735 var index = this.data.length;
13736 this.data.addAll(records);
13737 this.fireEvent("add", this, records, index);
13741 * Remove a Record from the Store and fires the remove event.
13742 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13744 remove : function(record){
13745 var index = this.data.indexOf(record);
13746 this.data.removeAt(index);
13748 if(this.pruneModifiedRecords){
13749 this.modified.remove(record);
13751 this.fireEvent("remove", this, record, index);
13755 * Remove all Records from the Store and fires the clear event.
13757 removeAll : function(){
13759 if(this.pruneModifiedRecords){
13760 this.modified = [];
13762 this.fireEvent("clear", this);
13766 * Inserts Records to the Store at the given index and fires the add event.
13767 * @param {Number} index The start index at which to insert the passed Records.
13768 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13770 insert : function(index, records){
13771 records = [].concat(records);
13772 for(var i = 0, len = records.length; i < len; i++){
13773 this.data.insert(index, records[i]);
13774 records[i].join(this);
13776 this.fireEvent("add", this, records, index);
13780 * Get the index within the cache of the passed Record.
13781 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13782 * @return {Number} The index of the passed Record. Returns -1 if not found.
13784 indexOf : function(record){
13785 return this.data.indexOf(record);
13789 * Get the index within the cache of the Record with the passed id.
13790 * @param {String} id The id of the Record to find.
13791 * @return {Number} The index of the Record. Returns -1 if not found.
13793 indexOfId : function(id){
13794 return this.data.indexOfKey(id);
13798 * Get the Record with the specified id.
13799 * @param {String} id The id of the Record to find.
13800 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13802 getById : function(id){
13803 return this.data.key(id);
13807 * Get the Record at the specified index.
13808 * @param {Number} index The index of the Record to find.
13809 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13811 getAt : function(index){
13812 return this.data.itemAt(index);
13816 * Returns a range of Records between specified indices.
13817 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13818 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13819 * @return {Roo.data.Record[]} An array of Records
13821 getRange : function(start, end){
13822 return this.data.getRange(start, end);
13826 storeOptions : function(o){
13827 o = Roo.apply({}, o);
13830 this.lastOptions = o;
13834 * Loads the Record cache from the configured Proxy using the configured Reader.
13836 * If using remote paging, then the first load call must specify the <em>start</em>
13837 * and <em>limit</em> properties in the options.params property to establish the initial
13838 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13840 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13841 * and this call will return before the new data has been loaded. Perform any post-processing
13842 * in a callback function, or in a "load" event handler.</strong>
13844 * @param {Object} options An object containing properties which control loading options:<ul>
13845 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13846 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13847 * passed the following arguments:<ul>
13848 * <li>r : Roo.data.Record[]</li>
13849 * <li>options: Options object from the load call</li>
13850 * <li>success: Boolean success indicator</li></ul></li>
13851 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13852 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13855 load : function(options){
13856 options = options || {};
13857 if(this.fireEvent("beforeload", this, options) !== false){
13858 this.storeOptions(options);
13859 var p = Roo.apply(options.params || {}, this.baseParams);
13860 // if meta was not loaded from remote source.. try requesting it.
13861 if (!this.reader.metaFromRemote) {
13862 p._requestMeta = 1;
13864 if(this.sortInfo && this.remoteSort){
13865 var pn = this.paramNames;
13866 p[pn["sort"]] = this.sortInfo.field;
13867 p[pn["dir"]] = this.sortInfo.direction;
13869 if (this.multiSort) {
13870 var pn = this.paramNames;
13871 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13874 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13879 * Reloads the Record cache from the configured Proxy using the configured Reader and
13880 * the options from the last load operation performed.
13881 * @param {Object} options (optional) An object containing properties which may override the options
13882 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13883 * the most recently used options are reused).
13885 reload : function(options){
13886 this.load(Roo.applyIf(options||{}, this.lastOptions));
13890 // Called as a callback by the Reader during a load operation.
13891 loadRecords : function(o, options, success){
13892 if(!o || success === false){
13893 if(success !== false){
13894 this.fireEvent("load", this, [], options, o);
13896 if(options.callback){
13897 options.callback.call(options.scope || this, [], options, false);
13901 // if data returned failure - throw an exception.
13902 if (o.success === false) {
13903 // show a message if no listener is registered.
13904 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13905 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13907 // loadmask wil be hooked into this..
13908 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13911 var r = o.records, t = o.totalRecords || r.length;
13913 this.fireEvent("beforeloadadd", this, r, options, o);
13915 if(!options || options.add !== true){
13916 if(this.pruneModifiedRecords){
13917 this.modified = [];
13919 for(var i = 0, len = r.length; i < len; i++){
13923 this.data = this.snapshot;
13924 delete this.snapshot;
13927 this.data.addAll(r);
13928 this.totalLength = t;
13930 this.fireEvent("datachanged", this);
13932 this.totalLength = Math.max(t, this.data.length+r.length);
13936 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13938 var e = new Roo.data.Record({});
13940 e.set(this.parent.displayField, this.parent.emptyTitle);
13941 e.set(this.parent.valueField, '');
13946 this.fireEvent("load", this, r, options, o);
13947 if(options.callback){
13948 options.callback.call(options.scope || this, r, options, true);
13954 * Loads data from a passed data block. A Reader which understands the format of the data
13955 * must have been configured in the constructor.
13956 * @param {Object} data The data block from which to read the Records. The format of the data expected
13957 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13958 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13960 loadData : function(o, append){
13961 var r = this.reader.readRecords(o);
13962 this.loadRecords(r, {add: append}, true);
13966 * using 'cn' the nested child reader read the child array into it's child stores.
13967 * @param {Object} rec The record with a 'children array
13969 loadDataFromChildren : function(rec)
13971 this.loadData(this.reader.toLoadData(rec));
13976 * Gets the number of cached records.
13978 * <em>If using paging, this may not be the total size of the dataset. If the data object
13979 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13980 * the data set size</em>
13982 getCount : function(){
13983 return this.data.length || 0;
13987 * Gets the total number of records in the dataset as returned by the server.
13989 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13990 * the dataset size</em>
13992 getTotalCount : function(){
13993 return this.totalLength || 0;
13997 * Returns the sort state of the Store as an object with two properties:
13999 field {String} The name of the field by which the Records are sorted
14000 direction {String} The sort order, "ASC" or "DESC"
14003 getSortState : function(){
14004 return this.sortInfo;
14008 applySort : function(){
14009 if(this.sortInfo && !this.remoteSort){
14010 var s = this.sortInfo, f = s.field;
14011 var st = this.fields.get(f).sortType;
14012 var fn = function(r1, r2){
14013 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14014 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14016 this.data.sort(s.direction, fn);
14017 if(this.snapshot && this.snapshot != this.data){
14018 this.snapshot.sort(s.direction, fn);
14024 * Sets the default sort column and order to be used by the next load operation.
14025 * @param {String} fieldName The name of the field to sort by.
14026 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14028 setDefaultSort : function(field, dir){
14029 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14033 * Sort the Records.
14034 * If remote sorting is used, the sort is performed on the server, and the cache is
14035 * reloaded. If local sorting is used, the cache is sorted internally.
14036 * @param {String} fieldName The name of the field to sort by.
14037 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14039 sort : function(fieldName, dir){
14040 var f = this.fields.get(fieldName);
14042 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14044 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14045 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14050 this.sortToggle[f.name] = dir;
14051 this.sortInfo = {field: f.name, direction: dir};
14052 if(!this.remoteSort){
14054 this.fireEvent("datachanged", this);
14056 this.load(this.lastOptions);
14061 * Calls the specified function for each of the Records in the cache.
14062 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14063 * Returning <em>false</em> aborts and exits the iteration.
14064 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14066 each : function(fn, scope){
14067 this.data.each(fn, scope);
14071 * Gets all records modified since the last commit. Modified records are persisted across load operations
14072 * (e.g., during paging).
14073 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14075 getModifiedRecords : function(){
14076 return this.modified;
14080 createFilterFn : function(property, value, anyMatch){
14081 if(!value.exec){ // not a regex
14082 value = String(value);
14083 if(value.length == 0){
14086 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14088 return function(r){
14089 return value.test(r.data[property]);
14094 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14095 * @param {String} property A field on your records
14096 * @param {Number} start The record index to start at (defaults to 0)
14097 * @param {Number} end The last record index to include (defaults to length - 1)
14098 * @return {Number} The sum
14100 sum : function(property, start, end){
14101 var rs = this.data.items, v = 0;
14102 start = start || 0;
14103 end = (end || end === 0) ? end : rs.length-1;
14105 for(var i = start; i <= end; i++){
14106 v += (rs[i].data[property] || 0);
14112 * Filter the records by a specified property.
14113 * @param {String} field A field on your records
14114 * @param {String/RegExp} value Either a string that the field
14115 * should start with or a RegExp to test against the field
14116 * @param {Boolean} anyMatch True to match any part not just the beginning
14118 filter : function(property, value, anyMatch){
14119 var fn = this.createFilterFn(property, value, anyMatch);
14120 return fn ? this.filterBy(fn) : this.clearFilter();
14124 * Filter by a function. The specified function will be called with each
14125 * record in this data source. If the function returns true the record is included,
14126 * otherwise it is filtered.
14127 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14128 * @param {Object} scope (optional) The scope of the function (defaults to this)
14130 filterBy : function(fn, scope){
14131 this.snapshot = this.snapshot || this.data;
14132 this.data = this.queryBy(fn, scope||this);
14133 this.fireEvent("datachanged", this);
14137 * Query the records by a specified property.
14138 * @param {String} field A field on your records
14139 * @param {String/RegExp} value Either a string that the field
14140 * should start with or a RegExp to test against the field
14141 * @param {Boolean} anyMatch True to match any part not just the beginning
14142 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14144 query : function(property, value, anyMatch){
14145 var fn = this.createFilterFn(property, value, anyMatch);
14146 return fn ? this.queryBy(fn) : this.data.clone();
14150 * Query by a function. The specified function will be called with each
14151 * record in this data source. If the function returns true the record is included
14153 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14154 * @param {Object} scope (optional) The scope of the function (defaults to this)
14155 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14157 queryBy : function(fn, scope){
14158 var data = this.snapshot || this.data;
14159 return data.filterBy(fn, scope||this);
14163 * Collects unique values for a particular dataIndex from this store.
14164 * @param {String} dataIndex The property to collect
14165 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14166 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14167 * @return {Array} An array of the unique values
14169 collect : function(dataIndex, allowNull, bypassFilter){
14170 var d = (bypassFilter === true && this.snapshot) ?
14171 this.snapshot.items : this.data.items;
14172 var v, sv, r = [], l = {};
14173 for(var i = 0, len = d.length; i < len; i++){
14174 v = d[i].data[dataIndex];
14176 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14185 * Revert to a view of the Record cache with no filtering applied.
14186 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14188 clearFilter : function(suppressEvent){
14189 if(this.snapshot && this.snapshot != this.data){
14190 this.data = this.snapshot;
14191 delete this.snapshot;
14192 if(suppressEvent !== true){
14193 this.fireEvent("datachanged", this);
14199 afterEdit : function(record){
14200 if(this.modified.indexOf(record) == -1){
14201 this.modified.push(record);
14203 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14207 afterReject : function(record){
14208 this.modified.remove(record);
14209 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14213 afterCommit : function(record){
14214 this.modified.remove(record);
14215 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14219 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14220 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14222 commitChanges : function(){
14223 var m = this.modified.slice(0);
14224 this.modified = [];
14225 for(var i = 0, len = m.length; i < len; i++){
14231 * Cancel outstanding changes on all changed records.
14233 rejectChanges : function(){
14234 var m = this.modified.slice(0);
14235 this.modified = [];
14236 for(var i = 0, len = m.length; i < len; i++){
14241 onMetaChange : function(meta, rtype, o){
14242 this.recordType = rtype;
14243 this.fields = rtype.prototype.fields;
14244 delete this.snapshot;
14245 this.sortInfo = meta.sortInfo || this.sortInfo;
14246 this.modified = [];
14247 this.fireEvent('metachange', this, this.reader.meta);
14250 moveIndex : function(data, type)
14252 var index = this.indexOf(data);
14254 var newIndex = index + type;
14258 this.insert(newIndex, data);
14263 * Ext JS Library 1.1.1
14264 * Copyright(c) 2006-2007, Ext JS, LLC.
14266 * Originally Released Under LGPL - original licence link has changed is not relivant.
14269 * <script type="text/javascript">
14273 * @class Roo.data.SimpleStore
14274 * @extends Roo.data.Store
14275 * Small helper class to make creating Stores from Array data easier.
14276 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14277 * @cfg {Array} fields An array of field definition objects, or field name strings.
14278 * @cfg {Object} an existing reader (eg. copied from another store)
14279 * @cfg {Array} data The multi-dimensional array of data
14281 * @param {Object} config
14283 Roo.data.SimpleStore = function(config)
14285 Roo.data.SimpleStore.superclass.constructor.call(this, {
14287 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14290 Roo.data.Record.create(config.fields)
14292 proxy : new Roo.data.MemoryProxy(config.data)
14296 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14298 * Ext JS Library 1.1.1
14299 * Copyright(c) 2006-2007, Ext JS, LLC.
14301 * Originally Released Under LGPL - original licence link has changed is not relivant.
14304 * <script type="text/javascript">
14309 * @extends Roo.data.Store
14310 * @class Roo.data.JsonStore
14311 * Small helper class to make creating Stores for JSON data easier. <br/>
14313 var store = new Roo.data.JsonStore({
14314 url: 'get-images.php',
14316 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14319 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14320 * JsonReader and HttpProxy (unless inline data is provided).</b>
14321 * @cfg {Array} fields An array of field definition objects, or field name strings.
14323 * @param {Object} config
14325 Roo.data.JsonStore = function(c){
14326 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14327 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14328 reader: new Roo.data.JsonReader(c, c.fields)
14331 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14333 * Ext JS Library 1.1.1
14334 * Copyright(c) 2006-2007, Ext JS, LLC.
14336 * Originally Released Under LGPL - original licence link has changed is not relivant.
14339 * <script type="text/javascript">
14343 Roo.data.Field = function(config){
14344 if(typeof config == "string"){
14345 config = {name: config};
14347 Roo.apply(this, config);
14350 this.type = "auto";
14353 var st = Roo.data.SortTypes;
14354 // named sortTypes are supported, here we look them up
14355 if(typeof this.sortType == "string"){
14356 this.sortType = st[this.sortType];
14359 // set default sortType for strings and dates
14360 if(!this.sortType){
14363 this.sortType = st.asUCString;
14366 this.sortType = st.asDate;
14369 this.sortType = st.none;
14374 var stripRe = /[\$,%]/g;
14376 // prebuilt conversion function for this field, instead of
14377 // switching every time we're reading a value
14379 var cv, dateFormat = this.dateFormat;
14384 cv = function(v){ return v; };
14387 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14391 return v !== undefined && v !== null && v !== '' ?
14392 parseInt(String(v).replace(stripRe, ""), 10) : '';
14397 return v !== undefined && v !== null && v !== '' ?
14398 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14403 cv = function(v){ return v === true || v === "true" || v == 1; };
14410 if(v instanceof Date){
14414 if(dateFormat == "timestamp"){
14415 return new Date(v*1000);
14417 return Date.parseDate(v, dateFormat);
14419 var parsed = Date.parse(v);
14420 return parsed ? new Date(parsed) : null;
14429 Roo.data.Field.prototype = {
14437 * Ext JS Library 1.1.1
14438 * Copyright(c) 2006-2007, Ext JS, LLC.
14440 * Originally Released Under LGPL - original licence link has changed is not relivant.
14443 * <script type="text/javascript">
14446 // Base class for reading structured data from a data source. This class is intended to be
14447 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14450 * @class Roo.data.DataReader
14451 * Base class for reading structured data from a data source. This class is intended to be
14452 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14455 Roo.data.DataReader = function(meta, recordType){
14459 this.recordType = recordType instanceof Array ?
14460 Roo.data.Record.create(recordType) : recordType;
14463 Roo.data.DataReader.prototype = {
14466 readerType : 'Data',
14468 * Create an empty record
14469 * @param {Object} data (optional) - overlay some values
14470 * @return {Roo.data.Record} record created.
14472 newRow : function(d) {
14474 this.recordType.prototype.fields.each(function(c) {
14476 case 'int' : da[c.name] = 0; break;
14477 case 'date' : da[c.name] = new Date(); break;
14478 case 'float' : da[c.name] = 0.0; break;
14479 case 'boolean' : da[c.name] = false; break;
14480 default : da[c.name] = ""; break;
14484 return new this.recordType(Roo.apply(da, d));
14490 * Ext JS Library 1.1.1
14491 * Copyright(c) 2006-2007, Ext JS, LLC.
14493 * Originally Released Under LGPL - original licence link has changed is not relivant.
14496 * <script type="text/javascript">
14500 * @class Roo.data.DataProxy
14501 * @extends Roo.data.Observable
14502 * This class is an abstract base class for implementations which provide retrieval of
14503 * unformatted data objects.<br>
14505 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14506 * (of the appropriate type which knows how to parse the data object) to provide a block of
14507 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14509 * Custom implementations must implement the load method as described in
14510 * {@link Roo.data.HttpProxy#load}.
14512 Roo.data.DataProxy = function(){
14515 * @event beforeload
14516 * Fires before a network request is made to retrieve a data object.
14517 * @param {Object} This DataProxy object.
14518 * @param {Object} params The params parameter to the load function.
14523 * Fires before the load method's callback is called.
14524 * @param {Object} This DataProxy object.
14525 * @param {Object} o The data object.
14526 * @param {Object} arg The callback argument object passed to the load function.
14530 * @event loadexception
14531 * Fires if an Exception occurs during data retrieval.
14532 * @param {Object} This DataProxy object.
14533 * @param {Object} o The data object.
14534 * @param {Object} arg The callback argument object passed to the load function.
14535 * @param {Object} e The Exception.
14537 loadexception : true
14539 Roo.data.DataProxy.superclass.constructor.call(this);
14542 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14545 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14549 * Ext JS Library 1.1.1
14550 * Copyright(c) 2006-2007, Ext JS, LLC.
14552 * Originally Released Under LGPL - original licence link has changed is not relivant.
14555 * <script type="text/javascript">
14558 * @class Roo.data.MemoryProxy
14559 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14560 * to the Reader when its load method is called.
14562 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14564 Roo.data.MemoryProxy = function(data){
14568 Roo.data.MemoryProxy.superclass.constructor.call(this);
14572 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14575 * Load data from the requested source (in this case an in-memory
14576 * data object passed to the constructor), read the data object into
14577 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14578 * process that block using the passed callback.
14579 * @param {Object} params This parameter is not used by the MemoryProxy class.
14580 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14581 * object into a block of Roo.data.Records.
14582 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14583 * The function must be passed <ul>
14584 * <li>The Record block object</li>
14585 * <li>The "arg" argument from the load function</li>
14586 * <li>A boolean success indicator</li>
14588 * @param {Object} scope The scope in which to call the callback
14589 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14591 load : function(params, reader, callback, scope, arg){
14592 params = params || {};
14595 result = reader.readRecords(params.data ? params.data :this.data);
14597 this.fireEvent("loadexception", this, arg, null, e);
14598 callback.call(scope, null, arg, false);
14601 callback.call(scope, result, arg, true);
14605 update : function(params, records){
14610 * Ext JS Library 1.1.1
14611 * Copyright(c) 2006-2007, Ext JS, LLC.
14613 * Originally Released Under LGPL - original licence link has changed is not relivant.
14616 * <script type="text/javascript">
14619 * @class Roo.data.HttpProxy
14620 * @extends Roo.data.DataProxy
14621 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14622 * configured to reference a certain URL.<br><br>
14624 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14625 * from which the running page was served.<br><br>
14627 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14629 * Be aware that to enable the browser to parse an XML document, the server must set
14630 * the Content-Type header in the HTTP response to "text/xml".
14632 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14633 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14634 * will be used to make the request.
14636 Roo.data.HttpProxy = function(conn){
14637 Roo.data.HttpProxy.superclass.constructor.call(this);
14638 // is conn a conn config or a real conn?
14640 this.useAjax = !conn || !conn.events;
14644 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14645 // thse are take from connection...
14648 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14651 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14652 * extra parameters to each request made by this object. (defaults to undefined)
14655 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14656 * to each request made by this object. (defaults to undefined)
14659 * @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)
14662 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14665 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14671 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14675 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14676 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14677 * a finer-grained basis than the DataProxy events.
14679 getConnection : function(){
14680 return this.useAjax ? Roo.Ajax : this.conn;
14684 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14685 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14686 * process that block using the passed callback.
14687 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14688 * for the request to the remote server.
14689 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14690 * object into a block of Roo.data.Records.
14691 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14692 * The function must be passed <ul>
14693 * <li>The Record block object</li>
14694 * <li>The "arg" argument from the load function</li>
14695 * <li>A boolean success indicator</li>
14697 * @param {Object} scope The scope in which to call the callback
14698 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14700 load : function(params, reader, callback, scope, arg){
14701 if(this.fireEvent("beforeload", this, params) !== false){
14703 params : params || {},
14705 callback : callback,
14710 callback : this.loadResponse,
14714 Roo.applyIf(o, this.conn);
14715 if(this.activeRequest){
14716 Roo.Ajax.abort(this.activeRequest);
14718 this.activeRequest = Roo.Ajax.request(o);
14720 this.conn.request(o);
14723 callback.call(scope||this, null, arg, false);
14728 loadResponse : function(o, success, response){
14729 delete this.activeRequest;
14731 this.fireEvent("loadexception", this, o, response);
14732 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14737 result = o.reader.read(response);
14739 this.fireEvent("loadexception", this, o, response, e);
14740 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14744 this.fireEvent("load", this, o, o.request.arg);
14745 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14749 update : function(dataSet){
14754 updateResponse : function(dataSet){
14759 * Ext JS Library 1.1.1
14760 * Copyright(c) 2006-2007, Ext JS, LLC.
14762 * Originally Released Under LGPL - original licence link has changed is not relivant.
14765 * <script type="text/javascript">
14769 * @class Roo.data.ScriptTagProxy
14770 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14771 * other than the originating domain of the running page.<br><br>
14773 * <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
14774 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14776 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14777 * source code that is used as the source inside a <script> tag.<br><br>
14779 * In order for the browser to process the returned data, the server must wrap the data object
14780 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14781 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14782 * depending on whether the callback name was passed:
14785 boolean scriptTag = false;
14786 String cb = request.getParameter("callback");
14789 response.setContentType("text/javascript");
14791 response.setContentType("application/x-json");
14793 Writer out = response.getWriter();
14795 out.write(cb + "(");
14797 out.print(dataBlock.toJsonString());
14804 * @param {Object} config A configuration object.
14806 Roo.data.ScriptTagProxy = function(config){
14807 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14808 Roo.apply(this, config);
14809 this.head = document.getElementsByTagName("head")[0];
14812 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14814 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14816 * @cfg {String} url The URL from which to request the data object.
14819 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14823 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14824 * the server the name of the callback function set up by the load call to process the returned data object.
14825 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14826 * javascript output which calls this named function passing the data object as its only parameter.
14828 callbackParam : "callback",
14830 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14831 * name to the request.
14836 * Load data from the configured URL, read the data object into
14837 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14838 * process that block using the passed callback.
14839 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14840 * for the request to the remote server.
14841 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14842 * object into a block of Roo.data.Records.
14843 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14844 * The function must be passed <ul>
14845 * <li>The Record block object</li>
14846 * <li>The "arg" argument from the load function</li>
14847 * <li>A boolean success indicator</li>
14849 * @param {Object} scope The scope in which to call the callback
14850 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14852 load : function(params, reader, callback, scope, arg){
14853 if(this.fireEvent("beforeload", this, params) !== false){
14855 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14857 var url = this.url;
14858 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14860 url += "&_dc=" + (new Date().getTime());
14862 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14865 cb : "stcCallback"+transId,
14866 scriptId : "stcScript"+transId,
14870 callback : callback,
14876 window[trans.cb] = function(o){
14877 conn.handleResponse(o, trans);
14880 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14882 if(this.autoAbort !== false){
14886 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14888 var script = document.createElement("script");
14889 script.setAttribute("src", url);
14890 script.setAttribute("type", "text/javascript");
14891 script.setAttribute("id", trans.scriptId);
14892 this.head.appendChild(script);
14894 this.trans = trans;
14896 callback.call(scope||this, null, arg, false);
14901 isLoading : function(){
14902 return this.trans ? true : false;
14906 * Abort the current server request.
14908 abort : function(){
14909 if(this.isLoading()){
14910 this.destroyTrans(this.trans);
14915 destroyTrans : function(trans, isLoaded){
14916 this.head.removeChild(document.getElementById(trans.scriptId));
14917 clearTimeout(trans.timeoutId);
14919 window[trans.cb] = undefined;
14921 delete window[trans.cb];
14924 // if hasn't been loaded, wait for load to remove it to prevent script error
14925 window[trans.cb] = function(){
14926 window[trans.cb] = undefined;
14928 delete window[trans.cb];
14935 handleResponse : function(o, trans){
14936 this.trans = false;
14937 this.destroyTrans(trans, true);
14940 result = trans.reader.readRecords(o);
14942 this.fireEvent("loadexception", this, o, trans.arg, e);
14943 trans.callback.call(trans.scope||window, null, trans.arg, false);
14946 this.fireEvent("load", this, o, trans.arg);
14947 trans.callback.call(trans.scope||window, result, trans.arg, true);
14951 handleFailure : function(trans){
14952 this.trans = false;
14953 this.destroyTrans(trans, false);
14954 this.fireEvent("loadexception", this, null, trans.arg);
14955 trans.callback.call(trans.scope||window, null, trans.arg, false);
14959 * Ext JS Library 1.1.1
14960 * Copyright(c) 2006-2007, Ext JS, LLC.
14962 * Originally Released Under LGPL - original licence link has changed is not relivant.
14965 * <script type="text/javascript">
14969 * @class Roo.data.JsonReader
14970 * @extends Roo.data.DataReader
14971 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14972 * based on mappings in a provided Roo.data.Record constructor.
14974 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14975 * in the reply previously.
14980 var RecordDef = Roo.data.Record.create([
14981 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14982 {name: 'occupation'} // This field will use "occupation" as the mapping.
14984 var myReader = new Roo.data.JsonReader({
14985 totalProperty: "results", // The property which contains the total dataset size (optional)
14986 root: "rows", // The property which contains an Array of row objects
14987 id: "id" // The property within each row object that provides an ID for the record (optional)
14991 * This would consume a JSON file like this:
14993 { 'results': 2, 'rows': [
14994 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14995 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14998 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14999 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15000 * paged from the remote server.
15001 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15002 * @cfg {String} root name of the property which contains the Array of row objects.
15003 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15004 * @cfg {Array} fields Array of field definition objects
15006 * Create a new JsonReader
15007 * @param {Object} meta Metadata configuration options
15008 * @param {Object} recordType Either an Array of field definition objects,
15009 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15011 Roo.data.JsonReader = function(meta, recordType){
15014 // set some defaults:
15015 Roo.applyIf(meta, {
15016 totalProperty: 'total',
15017 successProperty : 'success',
15022 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15024 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15026 readerType : 'Json',
15029 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15030 * Used by Store query builder to append _requestMeta to params.
15033 metaFromRemote : false,
15035 * This method is only used by a DataProxy which has retrieved data from a remote server.
15036 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15037 * @return {Object} data A data block which is used by an Roo.data.Store object as
15038 * a cache of Roo.data.Records.
15040 read : function(response){
15041 var json = response.responseText;
15043 var o = /* eval:var:o */ eval("("+json+")");
15045 throw {message: "JsonReader.read: Json object not found"};
15051 this.metaFromRemote = true;
15052 this.meta = o.metaData;
15053 this.recordType = Roo.data.Record.create(o.metaData.fields);
15054 this.onMetaChange(this.meta, this.recordType, o);
15056 return this.readRecords(o);
15059 // private function a store will implement
15060 onMetaChange : function(meta, recordType, o){
15067 simpleAccess: function(obj, subsc) {
15074 getJsonAccessor: function(){
15076 return function(expr) {
15078 return(re.test(expr))
15079 ? new Function("obj", "return obj." + expr)
15084 return Roo.emptyFn;
15089 * Create a data block containing Roo.data.Records from an XML document.
15090 * @param {Object} o An object which contains an Array of row objects in the property specified
15091 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15092 * which contains the total size of the dataset.
15093 * @return {Object} data A data block which is used by an Roo.data.Store object as
15094 * a cache of Roo.data.Records.
15096 readRecords : function(o){
15098 * After any data loads, the raw JSON data is available for further custom processing.
15102 var s = this.meta, Record = this.recordType,
15103 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15105 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15107 if(s.totalProperty) {
15108 this.getTotal = this.getJsonAccessor(s.totalProperty);
15110 if(s.successProperty) {
15111 this.getSuccess = this.getJsonAccessor(s.successProperty);
15113 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15115 var g = this.getJsonAccessor(s.id);
15116 this.getId = function(rec) {
15118 return (r === undefined || r === "") ? null : r;
15121 this.getId = function(){return null;};
15124 for(var jj = 0; jj < fl; jj++){
15126 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15127 this.ef[jj] = this.getJsonAccessor(map);
15131 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15132 if(s.totalProperty){
15133 var vt = parseInt(this.getTotal(o), 10);
15138 if(s.successProperty){
15139 var vs = this.getSuccess(o);
15140 if(vs === false || vs === 'false'){
15145 for(var i = 0; i < c; i++){
15148 var id = this.getId(n);
15149 for(var j = 0; j < fl; j++){
15151 var v = this.ef[j](n);
15153 Roo.log('missing convert for ' + f.name);
15157 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15159 var record = new Record(values, id);
15161 records[i] = record;
15167 totalRecords : totalRecords
15170 // used when loading children.. @see loadDataFromChildren
15171 toLoadData: function(rec)
15173 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15174 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15175 return { data : data, total : data.length };
15180 * Ext JS Library 1.1.1
15181 * Copyright(c) 2006-2007, Ext JS, LLC.
15183 * Originally Released Under LGPL - original licence link has changed is not relivant.
15186 * <script type="text/javascript">
15190 * @class Roo.data.ArrayReader
15191 * @extends Roo.data.DataReader
15192 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15193 * Each element of that Array represents a row of data fields. The
15194 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15195 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15199 var RecordDef = Roo.data.Record.create([
15200 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15201 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15203 var myReader = new Roo.data.ArrayReader({
15204 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15208 * This would consume an Array like this:
15210 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15214 * Create a new JsonReader
15215 * @param {Object} meta Metadata configuration options.
15216 * @param {Object|Array} recordType Either an Array of field definition objects
15218 * @cfg {Array} fields Array of field definition objects
15219 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15220 * as specified to {@link Roo.data.Record#create},
15221 * or an {@link Roo.data.Record} object
15224 * created using {@link Roo.data.Record#create}.
15226 Roo.data.ArrayReader = function(meta, recordType)
15228 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15231 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15234 * Create a data block containing Roo.data.Records from an XML document.
15235 * @param {Object} o An Array of row objects which represents the dataset.
15236 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15237 * a cache of Roo.data.Records.
15239 readRecords : function(o)
15241 var sid = this.meta ? this.meta.id : null;
15242 var recordType = this.recordType, fields = recordType.prototype.fields;
15245 for(var i = 0; i < root.length; i++){
15248 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15249 for(var j = 0, jlen = fields.length; j < jlen; j++){
15250 var f = fields.items[j];
15251 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15252 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15254 values[f.name] = v;
15256 var record = new recordType(values, id);
15258 records[records.length] = record;
15262 totalRecords : records.length
15265 // used when loading children.. @see loadDataFromChildren
15266 toLoadData: function(rec)
15268 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15269 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15280 * @class Roo.bootstrap.ComboBox
15281 * @extends Roo.bootstrap.TriggerField
15282 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15283 * @cfg {Boolean} append (true|false) default false
15284 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15285 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15286 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15287 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15288 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15289 * @cfg {Boolean} animate default true
15290 * @cfg {Boolean} emptyResultText only for touch device
15291 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15292 * @cfg {String} emptyTitle default ''
15293 * @cfg {Number} width fixed with? experimental
15295 * Create a new ComboBox.
15296 * @param {Object} config Configuration options
15298 Roo.bootstrap.ComboBox = function(config){
15299 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15303 * Fires when the dropdown list is expanded
15304 * @param {Roo.bootstrap.ComboBox} combo This combo box
15309 * Fires when the dropdown list is collapsed
15310 * @param {Roo.bootstrap.ComboBox} combo This combo box
15314 * @event beforeselect
15315 * Fires before a list item is selected. Return false to cancel the selection.
15316 * @param {Roo.bootstrap.ComboBox} combo This combo box
15317 * @param {Roo.data.Record} record The data record returned from the underlying store
15318 * @param {Number} index The index of the selected item in the dropdown list
15320 'beforeselect' : true,
15323 * Fires when a list item is selected
15324 * @param {Roo.bootstrap.ComboBox} combo This combo box
15325 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15326 * @param {Number} index The index of the selected item in the dropdown list
15330 * @event beforequery
15331 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15332 * The event object passed has these properties:
15333 * @param {Roo.bootstrap.ComboBox} combo This combo box
15334 * @param {String} query The query
15335 * @param {Boolean} forceAll true to force "all" query
15336 * @param {Boolean} cancel true to cancel the query
15337 * @param {Object} e The query event object
15339 'beforequery': true,
15342 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15343 * @param {Roo.bootstrap.ComboBox} combo This combo box
15348 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15349 * @param {Roo.bootstrap.ComboBox} combo This combo box
15350 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15355 * Fires when the remove value from the combobox array
15356 * @param {Roo.bootstrap.ComboBox} combo This combo box
15360 * @event afterremove
15361 * Fires when the remove value from the combobox array
15362 * @param {Roo.bootstrap.ComboBox} combo This combo box
15364 'afterremove' : true,
15366 * @event specialfilter
15367 * Fires when specialfilter
15368 * @param {Roo.bootstrap.ComboBox} combo This combo box
15370 'specialfilter' : true,
15373 * Fires when tick the element
15374 * @param {Roo.bootstrap.ComboBox} combo This combo box
15378 * @event touchviewdisplay
15379 * Fires when touch view require special display (default is using displayField)
15380 * @param {Roo.bootstrap.ComboBox} combo This combo box
15381 * @param {Object} cfg set html .
15383 'touchviewdisplay' : true
15388 this.tickItems = [];
15390 this.selectedIndex = -1;
15391 if(this.mode == 'local'){
15392 if(config.queryDelay === undefined){
15393 this.queryDelay = 10;
15395 if(config.minChars === undefined){
15401 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15404 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15405 * rendering into an Roo.Editor, defaults to false)
15408 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15409 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15412 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15415 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15416 * the dropdown list (defaults to undefined, with no header element)
15420 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15424 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15426 listWidth: undefined,
15428 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15429 * mode = 'remote' or 'text' if mode = 'local')
15431 displayField: undefined,
15434 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15435 * mode = 'remote' or 'value' if mode = 'local').
15436 * Note: use of a valueField requires the user make a selection
15437 * in order for a value to be mapped.
15439 valueField: undefined,
15441 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15446 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15447 * field's data value (defaults to the underlying DOM element's name)
15449 hiddenName: undefined,
15451 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15455 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15457 selectedClass: 'active',
15460 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15464 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15465 * anchor positions (defaults to 'tl-bl')
15467 listAlign: 'tl-bl?',
15469 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15473 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15474 * query specified by the allQuery config option (defaults to 'query')
15476 triggerAction: 'query',
15478 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15479 * (defaults to 4, does not apply if editable = false)
15483 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15484 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15488 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15489 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15493 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15494 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15498 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15499 * when editable = true (defaults to false)
15501 selectOnFocus:false,
15503 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15505 queryParam: 'query',
15507 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15508 * when mode = 'remote' (defaults to 'Loading...')
15510 loadingText: 'Loading...',
15512 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15516 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15520 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15521 * traditional select (defaults to true)
15525 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15529 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15533 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15534 * listWidth has a higher value)
15538 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15539 * allow the user to set arbitrary text into the field (defaults to false)
15541 forceSelection:false,
15543 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15544 * if typeAhead = true (defaults to 250)
15546 typeAheadDelay : 250,
15548 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15549 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15551 valueNotFoundText : undefined,
15553 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15555 blockFocus : false,
15558 * @cfg {Boolean} disableClear Disable showing of clear button.
15560 disableClear : false,
15562 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15564 alwaysQuery : false,
15567 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15572 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15574 invalidClass : "has-warning",
15577 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15579 validClass : "has-success",
15582 * @cfg {Boolean} specialFilter (true|false) special filter default false
15584 specialFilter : false,
15587 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15589 mobileTouchView : true,
15592 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15594 useNativeIOS : false,
15597 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15599 mobile_restrict_height : false,
15601 ios_options : false,
15613 btnPosition : 'right',
15614 triggerList : true,
15615 showToggleBtn : true,
15617 emptyResultText: 'Empty',
15618 triggerText : 'Select',
15622 // element that contains real text value.. (when hidden is used..)
15624 getAutoCreate : function()
15629 * Render classic select for iso
15632 if(Roo.isIOS && this.useNativeIOS){
15633 cfg = this.getAutoCreateNativeIOS();
15641 if(Roo.isTouch && this.mobileTouchView){
15642 cfg = this.getAutoCreateTouchView();
15649 if(!this.tickable){
15650 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15655 * ComboBox with tickable selections
15658 var align = this.labelAlign || this.parentLabelAlign();
15661 cls : 'form-group roo-combobox-tickable' //input-group
15664 var btn_text_select = '';
15665 var btn_text_done = '';
15666 var btn_text_cancel = '';
15668 if (this.btn_text_show) {
15669 btn_text_select = 'Select';
15670 btn_text_done = 'Done';
15671 btn_text_cancel = 'Cancel';
15676 cls : 'tickable-buttons',
15681 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15682 //html : this.triggerText
15683 html: btn_text_select
15689 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15691 html: btn_text_done
15697 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15699 html: btn_text_cancel
15705 buttons.cn.unshift({
15707 cls: 'roo-select2-search-field-input'
15713 Roo.each(buttons.cn, function(c){
15715 c.cls += ' btn-' + _this.size;
15718 if (_this.disabled) {
15725 style : 'display: contents',
15730 cls: 'form-hidden-field'
15734 cls: 'roo-select2-choices',
15738 cls: 'roo-select2-search-field',
15749 cls: 'roo-select2-container input-group roo-select2-container-multi',
15755 // cls: 'typeahead typeahead-long dropdown-menu',
15756 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15761 if(this.hasFeedback && !this.allowBlank){
15765 cls: 'glyphicon form-control-feedback'
15768 combobox.cn.push(feedback);
15775 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15776 tooltip : 'This field is required'
15778 if (Roo.bootstrap.version == 4) {
15781 style : 'display:none'
15784 if (align ==='left' && this.fieldLabel.length) {
15786 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15793 cls : 'control-label col-form-label',
15794 html : this.fieldLabel
15806 var labelCfg = cfg.cn[1];
15807 var contentCfg = cfg.cn[2];
15810 if(this.indicatorpos == 'right'){
15816 cls : 'control-label col-form-label',
15820 html : this.fieldLabel
15836 labelCfg = cfg.cn[0];
15837 contentCfg = cfg.cn[1];
15841 if(this.labelWidth > 12){
15842 labelCfg.style = "width: " + this.labelWidth + 'px';
15844 if(this.width * 1 > 0){
15845 contentCfg.style = "width: " + this.width + 'px';
15847 if(this.labelWidth < 13 && this.labelmd == 0){
15848 this.labelmd = this.labelWidth;
15851 if(this.labellg > 0){
15852 labelCfg.cls += ' col-lg-' + this.labellg;
15853 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15856 if(this.labelmd > 0){
15857 labelCfg.cls += ' col-md-' + this.labelmd;
15858 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15861 if(this.labelsm > 0){
15862 labelCfg.cls += ' col-sm-' + this.labelsm;
15863 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15866 if(this.labelxs > 0){
15867 labelCfg.cls += ' col-xs-' + this.labelxs;
15868 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15872 } else if ( this.fieldLabel.length) {
15873 // Roo.log(" label");
15878 //cls : 'input-group-addon',
15879 html : this.fieldLabel
15884 if(this.indicatorpos == 'right'){
15888 //cls : 'input-group-addon',
15889 html : this.fieldLabel
15899 // Roo.log(" no label && no align");
15906 ['xs','sm','md','lg'].map(function(size){
15907 if (settings[size]) {
15908 cfg.cls += ' col-' + size + '-' + settings[size];
15916 _initEventsCalled : false,
15919 initEvents: function()
15921 if (this._initEventsCalled) { // as we call render... prevent looping...
15924 this._initEventsCalled = true;
15927 throw "can not find store for combo";
15930 this.indicator = this.indicatorEl();
15932 this.store = Roo.factory(this.store, Roo.data);
15933 this.store.parent = this;
15935 // if we are building from html. then this element is so complex, that we can not really
15936 // use the rendered HTML.
15937 // so we have to trash and replace the previous code.
15938 if (Roo.XComponent.build_from_html) {
15939 // remove this element....
15940 var e = this.el.dom, k=0;
15941 while (e ) { e = e.previousSibling; ++k;}
15946 this.rendered = false;
15948 this.render(this.parent().getChildContainer(true), k);
15951 if(Roo.isIOS && this.useNativeIOS){
15952 this.initIOSView();
15960 if(Roo.isTouch && this.mobileTouchView){
15961 this.initTouchView();
15966 this.initTickableEvents();
15970 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15972 if(this.hiddenName){
15974 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15976 this.hiddenField.dom.value =
15977 this.hiddenValue !== undefined ? this.hiddenValue :
15978 this.value !== undefined ? this.value : '';
15980 // prevent input submission
15981 this.el.dom.removeAttribute('name');
15982 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15987 // this.el.dom.setAttribute('autocomplete', 'off');
15990 var cls = 'x-combo-list';
15992 //this.list = new Roo.Layer({
15993 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15999 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16000 _this.list.setWidth(lw);
16003 this.list.on('mouseover', this.onViewOver, this);
16004 this.list.on('mousemove', this.onViewMove, this);
16005 this.list.on('scroll', this.onViewScroll, this);
16008 this.list.swallowEvent('mousewheel');
16009 this.assetHeight = 0;
16012 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16013 this.assetHeight += this.header.getHeight();
16016 this.innerList = this.list.createChild({cls:cls+'-inner'});
16017 this.innerList.on('mouseover', this.onViewOver, this);
16018 this.innerList.on('mousemove', this.onViewMove, this);
16019 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16021 if(this.allowBlank && !this.pageSize && !this.disableClear){
16022 this.footer = this.list.createChild({cls:cls+'-ft'});
16023 this.pageTb = new Roo.Toolbar(this.footer);
16027 this.footer = this.list.createChild({cls:cls+'-ft'});
16028 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16029 {pageSize: this.pageSize});
16033 if (this.pageTb && this.allowBlank && !this.disableClear) {
16035 this.pageTb.add(new Roo.Toolbar.Fill(), {
16036 cls: 'x-btn-icon x-btn-clear',
16038 handler: function()
16041 _this.clearValue();
16042 _this.onSelect(false, -1);
16047 this.assetHeight += this.footer.getHeight();
16052 this.tpl = Roo.bootstrap.version == 4 ?
16053 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16054 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16057 this.view = new Roo.View(this.list, this.tpl, {
16058 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16060 //this.view.wrapEl.setDisplayed(false);
16061 this.view.on('click', this.onViewClick, this);
16064 this.store.on('beforeload', this.onBeforeLoad, this);
16065 this.store.on('load', this.onLoad, this);
16066 this.store.on('loadexception', this.onLoadException, this);
16068 if(this.resizable){
16069 this.resizer = new Roo.Resizable(this.list, {
16070 pinned:true, handles:'se'
16072 this.resizer.on('resize', function(r, w, h){
16073 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16074 this.listWidth = w;
16075 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16076 this.restrictHeight();
16078 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16081 if(!this.editable){
16082 this.editable = true;
16083 this.setEditable(false);
16088 if (typeof(this.events.add.listeners) != 'undefined') {
16090 this.addicon = this.wrap.createChild(
16091 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16093 this.addicon.on('click', function(e) {
16094 this.fireEvent('add', this);
16097 if (typeof(this.events.edit.listeners) != 'undefined') {
16099 this.editicon = this.wrap.createChild(
16100 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16101 if (this.addicon) {
16102 this.editicon.setStyle('margin-left', '40px');
16104 this.editicon.on('click', function(e) {
16106 // we fire even if inothing is selected..
16107 this.fireEvent('edit', this, this.lastData );
16113 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16114 "up" : function(e){
16115 this.inKeyMode = true;
16119 "down" : function(e){
16120 if(!this.isExpanded()){
16121 this.onTriggerClick();
16123 this.inKeyMode = true;
16128 "enter" : function(e){
16129 // this.onViewClick();
16133 if(this.fireEvent("specialkey", this, e)){
16134 this.onViewClick(false);
16140 "esc" : function(e){
16144 "tab" : function(e){
16147 if(this.fireEvent("specialkey", this, e)){
16148 this.onViewClick(false);
16156 doRelay : function(foo, bar, hname){
16157 if(hname == 'down' || this.scope.isExpanded()){
16158 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16167 this.queryDelay = Math.max(this.queryDelay || 10,
16168 this.mode == 'local' ? 10 : 250);
16171 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16173 if(this.typeAhead){
16174 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16176 if(this.editable !== false){
16177 this.inputEl().on("keyup", this.onKeyUp, this);
16179 if(this.forceSelection){
16180 this.inputEl().on('blur', this.doForce, this);
16184 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16185 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16189 initTickableEvents: function()
16193 if(this.hiddenName){
16195 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16197 this.hiddenField.dom.value =
16198 this.hiddenValue !== undefined ? this.hiddenValue :
16199 this.value !== undefined ? this.value : '';
16201 // prevent input submission
16202 this.el.dom.removeAttribute('name');
16203 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16208 // this.list = this.el.select('ul.dropdown-menu',true).first();
16210 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16211 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16212 if(this.triggerList){
16213 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16216 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16217 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16219 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16220 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16222 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16223 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16225 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16226 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16227 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16230 this.cancelBtn.hide();
16235 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16236 _this.list.setWidth(lw);
16239 this.list.on('mouseover', this.onViewOver, this);
16240 this.list.on('mousemove', this.onViewMove, this);
16242 this.list.on('scroll', this.onViewScroll, this);
16245 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16246 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16249 this.view = new Roo.View(this.list, this.tpl, {
16254 selectedClass: this.selectedClass
16257 //this.view.wrapEl.setDisplayed(false);
16258 this.view.on('click', this.onViewClick, this);
16262 this.store.on('beforeload', this.onBeforeLoad, this);
16263 this.store.on('load', this.onLoad, this);
16264 this.store.on('loadexception', this.onLoadException, this);
16267 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16268 "up" : function(e){
16269 this.inKeyMode = true;
16273 "down" : function(e){
16274 this.inKeyMode = true;
16278 "enter" : function(e){
16279 if(this.fireEvent("specialkey", this, e)){
16280 this.onViewClick(false);
16286 "esc" : function(e){
16287 this.onTickableFooterButtonClick(e, false, false);
16290 "tab" : function(e){
16291 this.fireEvent("specialkey", this, e);
16293 this.onTickableFooterButtonClick(e, false, false);
16300 doRelay : function(e, fn, key){
16301 if(this.scope.isExpanded()){
16302 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16311 this.queryDelay = Math.max(this.queryDelay || 10,
16312 this.mode == 'local' ? 10 : 250);
16315 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16317 if(this.typeAhead){
16318 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16321 if(this.editable !== false){
16322 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16325 this.indicator = this.indicatorEl();
16327 if(this.indicator){
16328 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16329 this.indicator.hide();
16334 onDestroy : function(){
16336 this.view.setStore(null);
16337 this.view.el.removeAllListeners();
16338 this.view.el.remove();
16339 this.view.purgeListeners();
16342 this.list.dom.innerHTML = '';
16346 this.store.un('beforeload', this.onBeforeLoad, this);
16347 this.store.un('load', this.onLoad, this);
16348 this.store.un('loadexception', this.onLoadException, this);
16350 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16354 fireKey : function(e){
16355 if(e.isNavKeyPress() && !this.list.isVisible()){
16356 this.fireEvent("specialkey", this, e);
16361 onResize: function(w, h)
16365 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16367 // if(typeof w != 'number'){
16368 // // we do not handle it!?!?
16371 // var tw = this.trigger.getWidth();
16372 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16373 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16375 // this.inputEl().setWidth( this.adjustWidth('input', x));
16377 // //this.trigger.setStyle('left', x+'px');
16379 // if(this.list && this.listWidth === undefined){
16380 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16381 // this.list.setWidth(lw);
16382 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16390 * Allow or prevent the user from directly editing the field text. If false is passed,
16391 * the user will only be able to select from the items defined in the dropdown list. This method
16392 * is the runtime equivalent of setting the 'editable' config option at config time.
16393 * @param {Boolean} value True to allow the user to directly edit the field text
16395 setEditable : function(value){
16396 if(value == this.editable){
16399 this.editable = value;
16401 this.inputEl().dom.setAttribute('readOnly', true);
16402 this.inputEl().on('mousedown', this.onTriggerClick, this);
16403 this.inputEl().addClass('x-combo-noedit');
16405 this.inputEl().dom.removeAttribute('readOnly');
16406 this.inputEl().un('mousedown', this.onTriggerClick, this);
16407 this.inputEl().removeClass('x-combo-noedit');
16413 onBeforeLoad : function(combo,opts){
16414 if(!this.hasFocus){
16418 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16420 this.restrictHeight();
16421 this.selectedIndex = -1;
16425 onLoad : function(){
16427 this.hasQuery = false;
16429 if(!this.hasFocus){
16433 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16434 this.loading.hide();
16437 if(this.store.getCount() > 0){
16440 this.restrictHeight();
16441 if(this.lastQuery == this.allQuery){
16442 if(this.editable && !this.tickable){
16443 this.inputEl().dom.select();
16447 !this.selectByValue(this.value, true) &&
16450 !this.store.lastOptions ||
16451 typeof(this.store.lastOptions.add) == 'undefined' ||
16452 this.store.lastOptions.add != true
16455 this.select(0, true);
16458 if(this.autoFocus){
16461 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16462 this.taTask.delay(this.typeAheadDelay);
16466 this.onEmptyResults();
16472 onLoadException : function()
16474 this.hasQuery = false;
16476 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16477 this.loading.hide();
16480 if(this.tickable && this.editable){
16485 // only causes errors at present
16486 //Roo.log(this.store.reader.jsonData);
16487 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16489 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16495 onTypeAhead : function(){
16496 if(this.store.getCount() > 0){
16497 var r = this.store.getAt(0);
16498 var newValue = r.data[this.displayField];
16499 var len = newValue.length;
16500 var selStart = this.getRawValue().length;
16502 if(selStart != len){
16503 this.setRawValue(newValue);
16504 this.selectText(selStart, newValue.length);
16510 onSelect : function(record, index){
16512 if(this.fireEvent('beforeselect', this, record, index) !== false){
16514 this.setFromData(index > -1 ? record.data : false);
16517 this.fireEvent('select', this, record, index);
16522 * Returns the currently selected field value or empty string if no value is set.
16523 * @return {String} value The selected value
16525 getValue : function()
16527 if(Roo.isIOS && this.useNativeIOS){
16528 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16532 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16535 if(this.valueField){
16536 return typeof this.value != 'undefined' ? this.value : '';
16538 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16542 getRawValue : function()
16544 if(Roo.isIOS && this.useNativeIOS){
16545 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16548 var v = this.inputEl().getValue();
16554 * Clears any text/value currently set in the field
16556 clearValue : function(){
16558 if(this.hiddenField){
16559 this.hiddenField.dom.value = '';
16562 this.setRawValue('');
16563 this.lastSelectionText = '';
16564 this.lastData = false;
16566 var close = this.closeTriggerEl();
16577 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16578 * will be displayed in the field. If the value does not match the data value of an existing item,
16579 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16580 * Otherwise the field will be blank (although the value will still be set).
16581 * @param {String} value The value to match
16583 setValue : function(v)
16585 if(Roo.isIOS && this.useNativeIOS){
16586 this.setIOSValue(v);
16596 if(this.valueField){
16597 var r = this.findRecord(this.valueField, v);
16599 text = r.data[this.displayField];
16600 }else if(this.valueNotFoundText !== undefined){
16601 text = this.valueNotFoundText;
16604 this.lastSelectionText = text;
16605 if(this.hiddenField){
16606 this.hiddenField.dom.value = v;
16608 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16611 var close = this.closeTriggerEl();
16614 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16620 * @property {Object} the last set data for the element
16625 * Sets the value of the field based on a object which is related to the record format for the store.
16626 * @param {Object} value the value to set as. or false on reset?
16628 setFromData : function(o){
16635 var dv = ''; // display value
16636 var vv = ''; // value value..
16638 if (this.displayField) {
16639 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16641 // this is an error condition!!!
16642 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16645 if(this.valueField){
16646 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16649 var close = this.closeTriggerEl();
16652 if(dv.length || vv * 1 > 0){
16654 this.blockFocus=true;
16660 if(this.hiddenField){
16661 this.hiddenField.dom.value = vv;
16663 this.lastSelectionText = dv;
16664 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16668 // no hidden field.. - we store the value in 'value', but still display
16669 // display field!!!!
16670 this.lastSelectionText = dv;
16671 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16678 reset : function(){
16679 // overridden so that last data is reset..
16686 this.setValue(this.originalValue);
16687 //this.clearInvalid();
16688 this.lastData = false;
16690 this.view.clearSelections();
16696 findRecord : function(prop, value){
16698 if(this.store.getCount() > 0){
16699 this.store.each(function(r){
16700 if(r.data[prop] == value){
16710 getName: function()
16712 // returns hidden if it's set..
16713 if (!this.rendered) {return ''};
16714 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16718 onViewMove : function(e, t){
16719 this.inKeyMode = false;
16723 onViewOver : function(e, t){
16724 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16727 var item = this.view.findItemFromChild(t);
16730 var index = this.view.indexOf(item);
16731 this.select(index, false);
16736 onViewClick : function(view, doFocus, el, e)
16738 var index = this.view.getSelectedIndexes()[0];
16740 var r = this.store.getAt(index);
16744 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16751 Roo.each(this.tickItems, function(v,k){
16753 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16755 _this.tickItems.splice(k, 1);
16757 if(typeof(e) == 'undefined' && view == false){
16758 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16770 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16771 this.tickItems.push(r.data);
16774 if(typeof(e) == 'undefined' && view == false){
16775 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16782 this.onSelect(r, index);
16784 if(doFocus !== false && !this.blockFocus){
16785 this.inputEl().focus();
16790 restrictHeight : function(){
16791 //this.innerList.dom.style.height = '';
16792 //var inner = this.innerList.dom;
16793 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16794 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16795 //this.list.beginUpdate();
16796 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16797 this.list.alignTo(this.inputEl(), this.listAlign);
16798 this.list.alignTo(this.inputEl(), this.listAlign);
16799 //this.list.endUpdate();
16803 onEmptyResults : function(){
16805 if(this.tickable && this.editable){
16806 this.hasFocus = false;
16807 this.restrictHeight();
16815 * Returns true if the dropdown list is expanded, else false.
16817 isExpanded : function(){
16818 return this.list.isVisible();
16822 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16823 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16824 * @param {String} value The data value of the item to select
16825 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16826 * selected item if it is not currently in view (defaults to true)
16827 * @return {Boolean} True if the value matched an item in the list, else false
16829 selectByValue : function(v, scrollIntoView){
16830 if(v !== undefined && v !== null){
16831 var r = this.findRecord(this.valueField || this.displayField, v);
16833 this.select(this.store.indexOf(r), scrollIntoView);
16841 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16842 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16843 * @param {Number} index The zero-based index of the list item to select
16844 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16845 * selected item if it is not currently in view (defaults to true)
16847 select : function(index, scrollIntoView){
16848 this.selectedIndex = index;
16849 this.view.select(index);
16850 if(scrollIntoView !== false){
16851 var el = this.view.getNode(index);
16853 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16856 this.list.scrollChildIntoView(el, false);
16862 selectNext : function(){
16863 var ct = this.store.getCount();
16865 if(this.selectedIndex == -1){
16867 }else if(this.selectedIndex < ct-1){
16868 this.select(this.selectedIndex+1);
16874 selectPrev : function(){
16875 var ct = this.store.getCount();
16877 if(this.selectedIndex == -1){
16879 }else if(this.selectedIndex != 0){
16880 this.select(this.selectedIndex-1);
16886 onKeyUp : function(e){
16887 if(this.editable !== false && !e.isSpecialKey()){
16888 this.lastKey = e.getKey();
16889 this.dqTask.delay(this.queryDelay);
16894 validateBlur : function(){
16895 return !this.list || !this.list.isVisible();
16899 initQuery : function(){
16901 var v = this.getRawValue();
16903 if(this.tickable && this.editable){
16904 v = this.tickableInputEl().getValue();
16911 doForce : function(){
16912 if(this.inputEl().dom.value.length > 0){
16913 this.inputEl().dom.value =
16914 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16920 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16921 * query allowing the query action to be canceled if needed.
16922 * @param {String} query The SQL query to execute
16923 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16924 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16925 * saved in the current store (defaults to false)
16927 doQuery : function(q, forceAll){
16929 if(q === undefined || q === null){
16934 forceAll: forceAll,
16938 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16943 forceAll = qe.forceAll;
16944 if(forceAll === true || (q.length >= this.minChars)){
16946 this.hasQuery = true;
16948 if(this.lastQuery != q || this.alwaysQuery){
16949 this.lastQuery = q;
16950 if(this.mode == 'local'){
16951 this.selectedIndex = -1;
16953 this.store.clearFilter();
16956 if(this.specialFilter){
16957 this.fireEvent('specialfilter', this);
16962 this.store.filter(this.displayField, q);
16965 this.store.fireEvent("datachanged", this.store);
16972 this.store.baseParams[this.queryParam] = q;
16974 var options = {params : this.getParams(q)};
16977 options.add = true;
16978 options.params.start = this.page * this.pageSize;
16981 this.store.load(options);
16984 * this code will make the page width larger, at the beginning, the list not align correctly,
16985 * we should expand the list on onLoad
16986 * so command out it
16991 this.selectedIndex = -1;
16996 this.loadNext = false;
17000 getParams : function(q){
17002 //p[this.queryParam] = q;
17006 p.limit = this.pageSize;
17012 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17014 collapse : function(){
17015 if(!this.isExpanded()){
17021 this.hasFocus = false;
17025 this.cancelBtn.hide();
17026 this.trigger.show();
17029 this.tickableInputEl().dom.value = '';
17030 this.tickableInputEl().blur();
17035 Roo.get(document).un('mousedown', this.collapseIf, this);
17036 Roo.get(document).un('mousewheel', this.collapseIf, this);
17037 if (!this.editable) {
17038 Roo.get(document).un('keydown', this.listKeyPress, this);
17040 this.fireEvent('collapse', this);
17046 collapseIf : function(e){
17047 var in_combo = e.within(this.el);
17048 var in_list = e.within(this.list);
17049 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17051 if (in_combo || in_list || is_list) {
17052 //e.stopPropagation();
17057 this.onTickableFooterButtonClick(e, false, false);
17065 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17067 expand : function(){
17069 if(this.isExpanded() || !this.hasFocus){
17073 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17074 this.list.setWidth(lw);
17080 this.restrictHeight();
17084 this.tickItems = Roo.apply([], this.item);
17087 this.cancelBtn.show();
17088 this.trigger.hide();
17091 this.tickableInputEl().focus();
17096 Roo.get(document).on('mousedown', this.collapseIf, this);
17097 Roo.get(document).on('mousewheel', this.collapseIf, this);
17098 if (!this.editable) {
17099 Roo.get(document).on('keydown', this.listKeyPress, this);
17102 this.fireEvent('expand', this);
17106 // Implements the default empty TriggerField.onTriggerClick function
17107 onTriggerClick : function(e)
17109 Roo.log('trigger click');
17111 if(this.disabled || !this.triggerList){
17116 this.loadNext = false;
17118 if(this.isExpanded()){
17120 if (!this.blockFocus) {
17121 this.inputEl().focus();
17125 this.hasFocus = true;
17126 if(this.triggerAction == 'all') {
17127 this.doQuery(this.allQuery, true);
17129 this.doQuery(this.getRawValue());
17131 if (!this.blockFocus) {
17132 this.inputEl().focus();
17137 onTickableTriggerClick : function(e)
17144 this.loadNext = false;
17145 this.hasFocus = true;
17147 if(this.triggerAction == 'all') {
17148 this.doQuery(this.allQuery, true);
17150 this.doQuery(this.getRawValue());
17154 onSearchFieldClick : function(e)
17156 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17157 this.onTickableFooterButtonClick(e, false, false);
17161 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17166 this.loadNext = false;
17167 this.hasFocus = true;
17169 if(this.triggerAction == 'all') {
17170 this.doQuery(this.allQuery, true);
17172 this.doQuery(this.getRawValue());
17176 listKeyPress : function(e)
17178 //Roo.log('listkeypress');
17179 // scroll to first matching element based on key pres..
17180 if (e.isSpecialKey()) {
17183 var k = String.fromCharCode(e.getKey()).toUpperCase();
17186 var csel = this.view.getSelectedNodes();
17187 var cselitem = false;
17189 var ix = this.view.indexOf(csel[0]);
17190 cselitem = this.store.getAt(ix);
17191 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17197 this.store.each(function(v) {
17199 // start at existing selection.
17200 if (cselitem.id == v.id) {
17206 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17207 match = this.store.indexOf(v);
17213 if (match === false) {
17214 return true; // no more action?
17217 this.view.select(match);
17218 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17219 sn.scrollIntoView(sn.dom.parentNode, false);
17222 onViewScroll : function(e, t){
17224 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){
17228 this.hasQuery = true;
17230 this.loading = this.list.select('.loading', true).first();
17232 if(this.loading === null){
17233 this.list.createChild({
17235 cls: 'loading roo-select2-more-results roo-select2-active',
17236 html: 'Loading more results...'
17239 this.loading = this.list.select('.loading', true).first();
17241 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17243 this.loading.hide();
17246 this.loading.show();
17251 this.loadNext = true;
17253 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17258 addItem : function(o)
17260 var dv = ''; // display value
17262 if (this.displayField) {
17263 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17265 // this is an error condition!!!
17266 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17273 var choice = this.choices.createChild({
17275 cls: 'roo-select2-search-choice',
17284 cls: 'roo-select2-search-choice-close fa fa-times',
17289 }, this.searchField);
17291 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17293 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17301 this.inputEl().dom.value = '';
17306 onRemoveItem : function(e, _self, o)
17308 e.preventDefault();
17310 this.lastItem = Roo.apply([], this.item);
17312 var index = this.item.indexOf(o.data) * 1;
17315 Roo.log('not this item?!');
17319 this.item.splice(index, 1);
17324 this.fireEvent('remove', this, e);
17330 syncValue : function()
17332 if(!this.item.length){
17339 Roo.each(this.item, function(i){
17340 if(_this.valueField){
17341 value.push(i[_this.valueField]);
17348 this.value = value.join(',');
17350 if(this.hiddenField){
17351 this.hiddenField.dom.value = this.value;
17354 this.store.fireEvent("datachanged", this.store);
17359 clearItem : function()
17361 if(!this.multiple){
17367 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17375 if(this.tickable && !Roo.isTouch){
17376 this.view.refresh();
17380 inputEl: function ()
17382 if(Roo.isIOS && this.useNativeIOS){
17383 return this.el.select('select.roo-ios-select', true).first();
17386 if(Roo.isTouch && this.mobileTouchView){
17387 return this.el.select('input.form-control',true).first();
17391 return this.searchField;
17394 return this.el.select('input.form-control',true).first();
17397 onTickableFooterButtonClick : function(e, btn, el)
17399 e.preventDefault();
17401 this.lastItem = Roo.apply([], this.item);
17403 if(btn && btn.name == 'cancel'){
17404 this.tickItems = Roo.apply([], this.item);
17413 Roo.each(this.tickItems, function(o){
17421 validate : function()
17423 if(this.getVisibilityEl().hasClass('hidden')){
17427 var v = this.getRawValue();
17430 v = this.getValue();
17433 if(this.disabled || this.allowBlank || v.length){
17438 this.markInvalid();
17442 tickableInputEl : function()
17444 if(!this.tickable || !this.editable){
17445 return this.inputEl();
17448 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17452 getAutoCreateTouchView : function()
17457 cls: 'form-group' //input-group
17463 type : this.inputType,
17464 cls : 'form-control x-combo-noedit',
17465 autocomplete: 'new-password',
17466 placeholder : this.placeholder || '',
17471 input.name = this.name;
17475 input.cls += ' input-' + this.size;
17478 if (this.disabled) {
17479 input.disabled = true;
17483 cls : 'roo-combobox-wrap',
17490 inputblock.cls += ' input-group';
17492 inputblock.cn.unshift({
17494 cls : 'input-group-addon input-group-prepend input-group-text',
17499 if(this.removable && !this.multiple){
17500 inputblock.cls += ' roo-removable';
17502 inputblock.cn.push({
17505 cls : 'roo-combo-removable-btn close'
17509 if(this.hasFeedback && !this.allowBlank){
17511 inputblock.cls += ' has-feedback';
17513 inputblock.cn.push({
17515 cls: 'glyphicon form-control-feedback'
17522 inputblock.cls += (this.before) ? '' : ' input-group';
17524 inputblock.cn.push({
17526 cls : 'input-group-addon input-group-append input-group-text',
17532 var ibwrap = inputblock;
17537 cls: 'roo-select2-choices',
17541 cls: 'roo-select2-search-field',
17554 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17559 cls: 'form-hidden-field'
17565 if(!this.multiple && this.showToggleBtn){
17571 if (this.caret != false) {
17574 cls: 'fa fa-' + this.caret
17581 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17583 Roo.bootstrap.version == 3 ? caret : '',
17586 cls: 'combobox-clear',
17600 combobox.cls += ' roo-select2-container-multi';
17603 var required = this.allowBlank ? {
17605 style: 'display: none'
17608 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17609 tooltip : 'This field is required'
17612 var align = this.labelAlign || this.parentLabelAlign();
17614 if (align ==='left' && this.fieldLabel.length) {
17620 cls : 'control-label col-form-label',
17621 html : this.fieldLabel
17625 cls : 'roo-combobox-wrap ',
17632 var labelCfg = cfg.cn[1];
17633 var contentCfg = cfg.cn[2];
17636 if(this.indicatorpos == 'right'){
17641 cls : 'control-label col-form-label',
17645 html : this.fieldLabel
17651 cls : "roo-combobox-wrap ",
17659 labelCfg = cfg.cn[0];
17660 contentCfg = cfg.cn[1];
17665 if(this.labelWidth > 12){
17666 labelCfg.style = "width: " + this.labelWidth + 'px';
17669 if(this.labelWidth < 13 && this.labelmd == 0){
17670 this.labelmd = this.labelWidth;
17673 if(this.labellg > 0){
17674 labelCfg.cls += ' col-lg-' + this.labellg;
17675 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17678 if(this.labelmd > 0){
17679 labelCfg.cls += ' col-md-' + this.labelmd;
17680 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17683 if(this.labelsm > 0){
17684 labelCfg.cls += ' col-sm-' + this.labelsm;
17685 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17688 if(this.labelxs > 0){
17689 labelCfg.cls += ' col-xs-' + this.labelxs;
17690 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17694 } else if ( this.fieldLabel.length) {
17699 cls : 'control-label',
17700 html : this.fieldLabel
17711 if(this.indicatorpos == 'right'){
17715 cls : 'control-label',
17716 html : this.fieldLabel,
17734 var settings = this;
17736 ['xs','sm','md','lg'].map(function(size){
17737 if (settings[size]) {
17738 cfg.cls += ' col-' + size + '-' + settings[size];
17745 initTouchView : function()
17747 this.renderTouchView();
17749 this.touchViewEl.on('scroll', function(){
17750 this.el.dom.scrollTop = 0;
17753 this.originalValue = this.getValue();
17755 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17757 this.inputEl().on("click", this.showTouchView, this);
17758 if (this.triggerEl) {
17759 this.triggerEl.on("click", this.showTouchView, this);
17763 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17764 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17766 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17768 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17769 this.store.on('load', this.onTouchViewLoad, this);
17770 this.store.on('loadexception', this.onTouchViewLoadException, this);
17772 if(this.hiddenName){
17774 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17776 this.hiddenField.dom.value =
17777 this.hiddenValue !== undefined ? this.hiddenValue :
17778 this.value !== undefined ? this.value : '';
17780 this.el.dom.removeAttribute('name');
17781 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17785 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17786 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17789 if(this.removable && !this.multiple){
17790 var close = this.closeTriggerEl();
17792 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17793 close.on('click', this.removeBtnClick, this, close);
17797 * fix the bug in Safari iOS8
17799 this.inputEl().on("focus", function(e){
17800 document.activeElement.blur();
17803 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17810 renderTouchView : function()
17812 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17813 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17815 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17816 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17818 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17819 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17820 this.touchViewBodyEl.setStyle('overflow', 'auto');
17822 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17823 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17825 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17826 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17830 showTouchView : function()
17836 this.touchViewHeaderEl.hide();
17838 if(this.modalTitle.length){
17839 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17840 this.touchViewHeaderEl.show();
17843 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17844 this.touchViewEl.show();
17846 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17848 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17849 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17851 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17853 if(this.modalTitle.length){
17854 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17857 this.touchViewBodyEl.setHeight(bodyHeight);
17861 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17863 this.touchViewEl.addClass(['in','show']);
17866 if(this._touchViewMask){
17867 Roo.get(document.body).addClass("x-body-masked");
17868 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17869 this._touchViewMask.setStyle('z-index', 10000);
17870 this._touchViewMask.addClass('show');
17873 this.doTouchViewQuery();
17877 hideTouchView : function()
17879 this.touchViewEl.removeClass(['in','show']);
17883 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17885 this.touchViewEl.setStyle('display', 'none');
17888 if(this._touchViewMask){
17889 this._touchViewMask.removeClass('show');
17890 Roo.get(document.body).removeClass("x-body-masked");
17894 setTouchViewValue : function()
17901 Roo.each(this.tickItems, function(o){
17906 this.hideTouchView();
17909 doTouchViewQuery : function()
17918 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17922 if(!this.alwaysQuery || this.mode == 'local'){
17923 this.onTouchViewLoad();
17930 onTouchViewBeforeLoad : function(combo,opts)
17936 onTouchViewLoad : function()
17938 if(this.store.getCount() < 1){
17939 this.onTouchViewEmptyResults();
17943 this.clearTouchView();
17945 var rawValue = this.getRawValue();
17947 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17949 this.tickItems = [];
17951 this.store.data.each(function(d, rowIndex){
17952 var row = this.touchViewListGroup.createChild(template);
17954 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17955 row.addClass(d.data.cls);
17958 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17961 html : d.data[this.displayField]
17964 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17965 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17968 row.removeClass('selected');
17969 if(!this.multiple && this.valueField &&
17970 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17973 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17974 row.addClass('selected');
17977 if(this.multiple && this.valueField &&
17978 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17982 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17983 this.tickItems.push(d.data);
17986 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17990 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17992 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17994 if(this.modalTitle.length){
17995 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17998 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18000 if(this.mobile_restrict_height && listHeight < bodyHeight){
18001 this.touchViewBodyEl.setHeight(listHeight);
18006 if(firstChecked && listHeight > bodyHeight){
18007 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18012 onTouchViewLoadException : function()
18014 this.hideTouchView();
18017 onTouchViewEmptyResults : function()
18019 this.clearTouchView();
18021 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18023 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18027 clearTouchView : function()
18029 this.touchViewListGroup.dom.innerHTML = '';
18032 onTouchViewClick : function(e, el, o)
18034 e.preventDefault();
18037 var rowIndex = o.rowIndex;
18039 var r = this.store.getAt(rowIndex);
18041 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18043 if(!this.multiple){
18044 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18045 c.dom.removeAttribute('checked');
18048 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18050 this.setFromData(r.data);
18052 var close = this.closeTriggerEl();
18058 this.hideTouchView();
18060 this.fireEvent('select', this, r, rowIndex);
18065 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18066 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18067 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18071 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18072 this.addItem(r.data);
18073 this.tickItems.push(r.data);
18077 getAutoCreateNativeIOS : function()
18080 cls: 'form-group' //input-group,
18085 cls : 'roo-ios-select'
18089 combobox.name = this.name;
18092 if (this.disabled) {
18093 combobox.disabled = true;
18096 var settings = this;
18098 ['xs','sm','md','lg'].map(function(size){
18099 if (settings[size]) {
18100 cfg.cls += ' col-' + size + '-' + settings[size];
18110 initIOSView : function()
18112 this.store.on('load', this.onIOSViewLoad, this);
18117 onIOSViewLoad : function()
18119 if(this.store.getCount() < 1){
18123 this.clearIOSView();
18125 if(this.allowBlank) {
18127 var default_text = '-- SELECT --';
18129 if(this.placeholder.length){
18130 default_text = this.placeholder;
18133 if(this.emptyTitle.length){
18134 default_text += ' - ' + this.emptyTitle + ' -';
18137 var opt = this.inputEl().createChild({
18140 html : default_text
18144 o[this.valueField] = 0;
18145 o[this.displayField] = default_text;
18147 this.ios_options.push({
18154 this.store.data.each(function(d, rowIndex){
18158 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18159 html = d.data[this.displayField];
18164 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18165 value = d.data[this.valueField];
18174 if(this.value == d.data[this.valueField]){
18175 option['selected'] = true;
18178 var opt = this.inputEl().createChild(option);
18180 this.ios_options.push({
18187 this.inputEl().on('change', function(){
18188 this.fireEvent('select', this);
18193 clearIOSView: function()
18195 this.inputEl().dom.innerHTML = '';
18197 this.ios_options = [];
18200 setIOSValue: function(v)
18204 if(!this.ios_options){
18208 Roo.each(this.ios_options, function(opts){
18210 opts.el.dom.removeAttribute('selected');
18212 if(opts.data[this.valueField] != v){
18216 opts.el.dom.setAttribute('selected', true);
18222 * @cfg {Boolean} grow
18226 * @cfg {Number} growMin
18230 * @cfg {Number} growMax
18239 Roo.apply(Roo.bootstrap.ComboBox, {
18243 cls: 'modal-header',
18265 cls: 'list-group-item',
18269 cls: 'roo-combobox-list-group-item-value'
18273 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18287 listItemCheckbox : {
18289 cls: 'list-group-item',
18293 cls: 'roo-combobox-list-group-item-value'
18297 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18313 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18318 cls: 'modal-footer',
18326 cls: 'col-xs-6 text-left',
18329 cls: 'btn btn-danger roo-touch-view-cancel',
18335 cls: 'col-xs-6 text-right',
18338 cls: 'btn btn-success roo-touch-view-ok',
18349 Roo.apply(Roo.bootstrap.ComboBox, {
18351 touchViewTemplate : {
18353 cls: 'modal fade roo-combobox-touch-view',
18357 cls: 'modal-dialog',
18358 style : 'position:fixed', // we have to fix position....
18362 cls: 'modal-content',
18364 Roo.bootstrap.ComboBox.header,
18365 Roo.bootstrap.ComboBox.body,
18366 Roo.bootstrap.ComboBox.footer
18375 * Ext JS Library 1.1.1
18376 * Copyright(c) 2006-2007, Ext JS, LLC.
18378 * Originally Released Under LGPL - original licence link has changed is not relivant.
18381 * <script type="text/javascript">
18386 * @extends Roo.util.Observable
18387 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18388 * This class also supports single and multi selection modes. <br>
18389 * Create a data model bound view:
18391 var store = new Roo.data.Store(...);
18393 var view = new Roo.View({
18395 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18397 singleSelect: true,
18398 selectedClass: "ydataview-selected",
18402 // listen for node click?
18403 view.on("click", function(vw, index, node, e){
18404 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18408 dataModel.load("foobar.xml");
18410 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18412 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18413 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18415 * Note: old style constructor is still suported (container, template, config)
18418 * Create a new View
18419 * @param {Object} config The config object
18422 Roo.View = function(config, depreciated_tpl, depreciated_config){
18424 this.parent = false;
18426 if (typeof(depreciated_tpl) == 'undefined') {
18427 // new way.. - universal constructor.
18428 Roo.apply(this, config);
18429 this.el = Roo.get(this.el);
18432 this.el = Roo.get(config);
18433 this.tpl = depreciated_tpl;
18434 Roo.apply(this, depreciated_config);
18436 this.wrapEl = this.el.wrap().wrap();
18437 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18440 if(typeof(this.tpl) == "string"){
18441 this.tpl = new Roo.Template(this.tpl);
18443 // support xtype ctors..
18444 this.tpl = new Roo.factory(this.tpl, Roo);
18448 this.tpl.compile();
18453 * @event beforeclick
18454 * Fires before a click is processed. Returns false to cancel the default action.
18455 * @param {Roo.View} this
18456 * @param {Number} index The index of the target node
18457 * @param {HTMLElement} node The target node
18458 * @param {Roo.EventObject} e The raw event object
18460 "beforeclick" : true,
18463 * Fires when a template node is clicked.
18464 * @param {Roo.View} this
18465 * @param {Number} index The index of the target node
18466 * @param {HTMLElement} node The target node
18467 * @param {Roo.EventObject} e The raw event object
18472 * Fires when a template node is double clicked.
18473 * @param {Roo.View} this
18474 * @param {Number} index The index of the target node
18475 * @param {HTMLElement} node The target node
18476 * @param {Roo.EventObject} e The raw event object
18480 * @event contextmenu
18481 * Fires when a template node is right clicked.
18482 * @param {Roo.View} this
18483 * @param {Number} index The index of the target node
18484 * @param {HTMLElement} node The target node
18485 * @param {Roo.EventObject} e The raw event object
18487 "contextmenu" : true,
18489 * @event selectionchange
18490 * Fires when the selected nodes change.
18491 * @param {Roo.View} this
18492 * @param {Array} selections Array of the selected nodes
18494 "selectionchange" : true,
18497 * @event beforeselect
18498 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18499 * @param {Roo.View} this
18500 * @param {HTMLElement} node The node to be selected
18501 * @param {Array} selections Array of currently selected nodes
18503 "beforeselect" : true,
18505 * @event preparedata
18506 * Fires on every row to render, to allow you to change the data.
18507 * @param {Roo.View} this
18508 * @param {Object} data to be rendered (change this)
18510 "preparedata" : true
18518 "click": this.onClick,
18519 "dblclick": this.onDblClick,
18520 "contextmenu": this.onContextMenu,
18524 this.selections = [];
18526 this.cmp = new Roo.CompositeElementLite([]);
18528 this.store = Roo.factory(this.store, Roo.data);
18529 this.setStore(this.store, true);
18532 if ( this.footer && this.footer.xtype) {
18534 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18536 this.footer.dataSource = this.store;
18537 this.footer.container = fctr;
18538 this.footer = Roo.factory(this.footer, Roo);
18539 fctr.insertFirst(this.el);
18541 // this is a bit insane - as the paging toolbar seems to detach the el..
18542 // dom.parentNode.parentNode.parentNode
18543 // they get detached?
18547 Roo.View.superclass.constructor.call(this);
18552 Roo.extend(Roo.View, Roo.util.Observable, {
18555 * @cfg {Roo.data.Store} store Data store to load data from.
18560 * @cfg {String|Roo.Element} el The container element.
18565 * @cfg {String|Roo.Template} tpl The template used by this View
18569 * @cfg {String} dataName the named area of the template to use as the data area
18570 * Works with domtemplates roo-name="name"
18574 * @cfg {String} selectedClass The css class to add to selected nodes
18576 selectedClass : "x-view-selected",
18578 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18583 * @cfg {String} text to display on mask (default Loading)
18587 * @cfg {Boolean} multiSelect Allow multiple selection
18589 multiSelect : false,
18591 * @cfg {Boolean} singleSelect Allow single selection
18593 singleSelect: false,
18596 * @cfg {Boolean} toggleSelect - selecting
18598 toggleSelect : false,
18601 * @cfg {Boolean} tickable - selecting
18606 * Returns the element this view is bound to.
18607 * @return {Roo.Element}
18609 getEl : function(){
18610 return this.wrapEl;
18616 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18618 refresh : function(){
18619 //Roo.log('refresh');
18622 // if we are using something like 'domtemplate', then
18623 // the what gets used is:
18624 // t.applySubtemplate(NAME, data, wrapping data..)
18625 // the outer template then get' applied with
18626 // the store 'extra data'
18627 // and the body get's added to the
18628 // roo-name="data" node?
18629 // <span class='roo-tpl-{name}'></span> ?????
18633 this.clearSelections();
18634 this.el.update("");
18636 var records = this.store.getRange();
18637 if(records.length < 1) {
18639 // is this valid?? = should it render a template??
18641 this.el.update(this.emptyText);
18645 if (this.dataName) {
18646 this.el.update(t.apply(this.store.meta)); //????
18647 el = this.el.child('.roo-tpl-' + this.dataName);
18650 for(var i = 0, len = records.length; i < len; i++){
18651 var data = this.prepareData(records[i].data, i, records[i]);
18652 this.fireEvent("preparedata", this, data, i, records[i]);
18654 var d = Roo.apply({}, data);
18657 Roo.apply(d, {'roo-id' : Roo.id()});
18661 Roo.each(this.parent.item, function(item){
18662 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18665 Roo.apply(d, {'roo-data-checked' : 'checked'});
18669 html[html.length] = Roo.util.Format.trim(
18671 t.applySubtemplate(this.dataName, d, this.store.meta) :
18678 el.update(html.join(""));
18679 this.nodes = el.dom.childNodes;
18680 this.updateIndexes(0);
18685 * Function to override to reformat the data that is sent to
18686 * the template for each node.
18687 * DEPRICATED - use the preparedata event handler.
18688 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18689 * a JSON object for an UpdateManager bound view).
18691 prepareData : function(data, index, record)
18693 this.fireEvent("preparedata", this, data, index, record);
18697 onUpdate : function(ds, record){
18698 // Roo.log('on update');
18699 this.clearSelections();
18700 var index = this.store.indexOf(record);
18701 var n = this.nodes[index];
18702 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18703 n.parentNode.removeChild(n);
18704 this.updateIndexes(index, index);
18710 onAdd : function(ds, records, index)
18712 //Roo.log(['on Add', ds, records, index] );
18713 this.clearSelections();
18714 if(this.nodes.length == 0){
18718 var n = this.nodes[index];
18719 for(var i = 0, len = records.length; i < len; i++){
18720 var d = this.prepareData(records[i].data, i, records[i]);
18722 this.tpl.insertBefore(n, d);
18725 this.tpl.append(this.el, d);
18728 this.updateIndexes(index);
18731 onRemove : function(ds, record, index){
18732 // Roo.log('onRemove');
18733 this.clearSelections();
18734 var el = this.dataName ?
18735 this.el.child('.roo-tpl-' + this.dataName) :
18738 el.dom.removeChild(this.nodes[index]);
18739 this.updateIndexes(index);
18743 * Refresh an individual node.
18744 * @param {Number} index
18746 refreshNode : function(index){
18747 this.onUpdate(this.store, this.store.getAt(index));
18750 updateIndexes : function(startIndex, endIndex){
18751 var ns = this.nodes;
18752 startIndex = startIndex || 0;
18753 endIndex = endIndex || ns.length - 1;
18754 for(var i = startIndex; i <= endIndex; i++){
18755 ns[i].nodeIndex = i;
18760 * Changes the data store this view uses and refresh the view.
18761 * @param {Store} store
18763 setStore : function(store, initial){
18764 if(!initial && this.store){
18765 this.store.un("datachanged", this.refresh);
18766 this.store.un("add", this.onAdd);
18767 this.store.un("remove", this.onRemove);
18768 this.store.un("update", this.onUpdate);
18769 this.store.un("clear", this.refresh);
18770 this.store.un("beforeload", this.onBeforeLoad);
18771 this.store.un("load", this.onLoad);
18772 this.store.un("loadexception", this.onLoad);
18776 store.on("datachanged", this.refresh, this);
18777 store.on("add", this.onAdd, this);
18778 store.on("remove", this.onRemove, this);
18779 store.on("update", this.onUpdate, this);
18780 store.on("clear", this.refresh, this);
18781 store.on("beforeload", this.onBeforeLoad, this);
18782 store.on("load", this.onLoad, this);
18783 store.on("loadexception", this.onLoad, this);
18791 * onbeforeLoad - masks the loading area.
18794 onBeforeLoad : function(store,opts)
18796 //Roo.log('onBeforeLoad');
18798 this.el.update("");
18800 this.el.mask(this.mask ? this.mask : "Loading" );
18802 onLoad : function ()
18809 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18810 * @param {HTMLElement} node
18811 * @return {HTMLElement} The template node
18813 findItemFromChild : function(node){
18814 var el = this.dataName ?
18815 this.el.child('.roo-tpl-' + this.dataName,true) :
18818 if(!node || node.parentNode == el){
18821 var p = node.parentNode;
18822 while(p && p != el){
18823 if(p.parentNode == el){
18832 onClick : function(e){
18833 var item = this.findItemFromChild(e.getTarget());
18835 var index = this.indexOf(item);
18836 if(this.onItemClick(item, index, e) !== false){
18837 this.fireEvent("click", this, index, item, e);
18840 this.clearSelections();
18845 onContextMenu : function(e){
18846 var item = this.findItemFromChild(e.getTarget());
18848 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18853 onDblClick : function(e){
18854 var item = this.findItemFromChild(e.getTarget());
18856 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18860 onItemClick : function(item, index, e)
18862 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18865 if (this.toggleSelect) {
18866 var m = this.isSelected(item) ? 'unselect' : 'select';
18869 _t[m](item, true, false);
18872 if(this.multiSelect || this.singleSelect){
18873 if(this.multiSelect && e.shiftKey && this.lastSelection){
18874 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18876 this.select(item, this.multiSelect && e.ctrlKey);
18877 this.lastSelection = item;
18880 if(!this.tickable){
18881 e.preventDefault();
18889 * Get the number of selected nodes.
18892 getSelectionCount : function(){
18893 return this.selections.length;
18897 * Get the currently selected nodes.
18898 * @return {Array} An array of HTMLElements
18900 getSelectedNodes : function(){
18901 return this.selections;
18905 * Get the indexes of the selected nodes.
18908 getSelectedIndexes : function(){
18909 var indexes = [], s = this.selections;
18910 for(var i = 0, len = s.length; i < len; i++){
18911 indexes.push(s[i].nodeIndex);
18917 * Clear all selections
18918 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18920 clearSelections : function(suppressEvent){
18921 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18922 this.cmp.elements = this.selections;
18923 this.cmp.removeClass(this.selectedClass);
18924 this.selections = [];
18925 if(!suppressEvent){
18926 this.fireEvent("selectionchange", this, this.selections);
18932 * Returns true if the passed node is selected
18933 * @param {HTMLElement/Number} node The node or node index
18934 * @return {Boolean}
18936 isSelected : function(node){
18937 var s = this.selections;
18941 node = this.getNode(node);
18942 return s.indexOf(node) !== -1;
18947 * @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
18948 * @param {Boolean} keepExisting (optional) true to keep existing selections
18949 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18951 select : function(nodeInfo, keepExisting, suppressEvent){
18952 if(nodeInfo instanceof Array){
18954 this.clearSelections(true);
18956 for(var i = 0, len = nodeInfo.length; i < len; i++){
18957 this.select(nodeInfo[i], true, true);
18961 var node = this.getNode(nodeInfo);
18962 if(!node || this.isSelected(node)){
18963 return; // already selected.
18966 this.clearSelections(true);
18969 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18970 Roo.fly(node).addClass(this.selectedClass);
18971 this.selections.push(node);
18972 if(!suppressEvent){
18973 this.fireEvent("selectionchange", this, this.selections);
18981 * @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
18982 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18983 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18985 unselect : function(nodeInfo, keepExisting, suppressEvent)
18987 if(nodeInfo instanceof Array){
18988 Roo.each(this.selections, function(s) {
18989 this.unselect(s, nodeInfo);
18993 var node = this.getNode(nodeInfo);
18994 if(!node || !this.isSelected(node)){
18995 //Roo.log("not selected");
18996 return; // not selected.
19000 Roo.each(this.selections, function(s) {
19002 Roo.fly(node).removeClass(this.selectedClass);
19009 this.selections= ns;
19010 this.fireEvent("selectionchange", this, this.selections);
19014 * Gets a template node.
19015 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19016 * @return {HTMLElement} The node or null if it wasn't found
19018 getNode : function(nodeInfo){
19019 if(typeof nodeInfo == "string"){
19020 return document.getElementById(nodeInfo);
19021 }else if(typeof nodeInfo == "number"){
19022 return this.nodes[nodeInfo];
19028 * Gets a range template nodes.
19029 * @param {Number} startIndex
19030 * @param {Number} endIndex
19031 * @return {Array} An array of nodes
19033 getNodes : function(start, end){
19034 var ns = this.nodes;
19035 start = start || 0;
19036 end = typeof end == "undefined" ? ns.length - 1 : end;
19039 for(var i = start; i <= end; i++){
19043 for(var i = start; i >= end; i--){
19051 * Finds the index of the passed node
19052 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19053 * @return {Number} The index of the node or -1
19055 indexOf : function(node){
19056 node = this.getNode(node);
19057 if(typeof node.nodeIndex == "number"){
19058 return node.nodeIndex;
19060 var ns = this.nodes;
19061 for(var i = 0, len = ns.length; i < len; i++){
19072 * based on jquery fullcalendar
19076 Roo.bootstrap = Roo.bootstrap || {};
19078 * @class Roo.bootstrap.Calendar
19079 * @extends Roo.bootstrap.Component
19080 * Bootstrap Calendar class
19081 * @cfg {Boolean} loadMask (true|false) default false
19082 * @cfg {Object} header generate the user specific header of the calendar, default false
19085 * Create a new Container
19086 * @param {Object} config The config object
19091 Roo.bootstrap.Calendar = function(config){
19092 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19096 * Fires when a date is selected
19097 * @param {DatePicker} this
19098 * @param {Date} date The selected date
19102 * @event monthchange
19103 * Fires when the displayed month changes
19104 * @param {DatePicker} this
19105 * @param {Date} date The selected month
19107 'monthchange': true,
19109 * @event evententer
19110 * Fires when mouse over an event
19111 * @param {Calendar} this
19112 * @param {event} Event
19114 'evententer': true,
19116 * @event eventleave
19117 * Fires when the mouse leaves an
19118 * @param {Calendar} this
19121 'eventleave': true,
19123 * @event eventclick
19124 * Fires when the mouse click an
19125 * @param {Calendar} this
19134 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19137 * @cfg {Number} startDay
19138 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19146 getAutoCreate : function(){
19149 var fc_button = function(name, corner, style, content ) {
19150 return Roo.apply({},{
19152 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19154 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19157 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19168 style : 'width:100%',
19175 cls : 'fc-header-left',
19177 fc_button('prev', 'left', 'arrow', '‹' ),
19178 fc_button('next', 'right', 'arrow', '›' ),
19179 { tag: 'span', cls: 'fc-header-space' },
19180 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19188 cls : 'fc-header-center',
19192 cls: 'fc-header-title',
19195 html : 'month / year'
19203 cls : 'fc-header-right',
19205 /* fc_button('month', 'left', '', 'month' ),
19206 fc_button('week', '', '', 'week' ),
19207 fc_button('day', 'right', '', 'day' )
19219 header = this.header;
19222 var cal_heads = function() {
19224 // fixme - handle this.
19226 for (var i =0; i < Date.dayNames.length; i++) {
19227 var d = Date.dayNames[i];
19230 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19231 html : d.substring(0,3)
19235 ret[0].cls += ' fc-first';
19236 ret[6].cls += ' fc-last';
19239 var cal_cell = function(n) {
19242 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19247 cls: 'fc-day-number',
19251 cls: 'fc-day-content',
19255 style: 'position: relative;' // height: 17px;
19267 var cal_rows = function() {
19270 for (var r = 0; r < 6; r++) {
19277 for (var i =0; i < Date.dayNames.length; i++) {
19278 var d = Date.dayNames[i];
19279 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19282 row.cn[0].cls+=' fc-first';
19283 row.cn[0].cn[0].style = 'min-height:90px';
19284 row.cn[6].cls+=' fc-last';
19288 ret[0].cls += ' fc-first';
19289 ret[4].cls += ' fc-prev-last';
19290 ret[5].cls += ' fc-last';
19297 cls: 'fc-border-separate',
19298 style : 'width:100%',
19306 cls : 'fc-first fc-last',
19324 cls : 'fc-content',
19325 style : "position: relative;",
19328 cls : 'fc-view fc-view-month fc-grid',
19329 style : 'position: relative',
19330 unselectable : 'on',
19333 cls : 'fc-event-container',
19334 style : 'position:absolute;z-index:8;top:0;left:0;'
19352 initEvents : function()
19355 throw "can not find store for calendar";
19361 style: "text-align:center",
19365 style: "background-color:white;width:50%;margin:250 auto",
19369 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19380 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19382 var size = this.el.select('.fc-content', true).first().getSize();
19383 this.maskEl.setSize(size.width, size.height);
19384 this.maskEl.enableDisplayMode("block");
19385 if(!this.loadMask){
19386 this.maskEl.hide();
19389 this.store = Roo.factory(this.store, Roo.data);
19390 this.store.on('load', this.onLoad, this);
19391 this.store.on('beforeload', this.onBeforeLoad, this);
19395 this.cells = this.el.select('.fc-day',true);
19396 //Roo.log(this.cells);
19397 this.textNodes = this.el.query('.fc-day-number');
19398 this.cells.addClassOnOver('fc-state-hover');
19400 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19401 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19402 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19403 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19405 this.on('monthchange', this.onMonthChange, this);
19407 this.update(new Date().clearTime());
19410 resize : function() {
19411 var sz = this.el.getSize();
19413 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19414 this.el.select('.fc-day-content div',true).setHeight(34);
19419 showPrevMonth : function(e){
19420 this.update(this.activeDate.add("mo", -1));
19422 showToday : function(e){
19423 this.update(new Date().clearTime());
19426 showNextMonth : function(e){
19427 this.update(this.activeDate.add("mo", 1));
19431 showPrevYear : function(){
19432 this.update(this.activeDate.add("y", -1));
19436 showNextYear : function(){
19437 this.update(this.activeDate.add("y", 1));
19442 update : function(date)
19444 var vd = this.activeDate;
19445 this.activeDate = date;
19446 // if(vd && this.el){
19447 // var t = date.getTime();
19448 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19449 // Roo.log('using add remove');
19451 // this.fireEvent('monthchange', this, date);
19453 // this.cells.removeClass("fc-state-highlight");
19454 // this.cells.each(function(c){
19455 // if(c.dateValue == t){
19456 // c.addClass("fc-state-highlight");
19457 // setTimeout(function(){
19458 // try{c.dom.firstChild.focus();}catch(e){}
19468 var days = date.getDaysInMonth();
19470 var firstOfMonth = date.getFirstDateOfMonth();
19471 var startingPos = firstOfMonth.getDay()-this.startDay;
19473 if(startingPos < this.startDay){
19477 var pm = date.add(Date.MONTH, -1);
19478 var prevStart = pm.getDaysInMonth()-startingPos;
19480 this.cells = this.el.select('.fc-day',true);
19481 this.textNodes = this.el.query('.fc-day-number');
19482 this.cells.addClassOnOver('fc-state-hover');
19484 var cells = this.cells.elements;
19485 var textEls = this.textNodes;
19487 Roo.each(cells, function(cell){
19488 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19491 days += startingPos;
19493 // convert everything to numbers so it's fast
19494 var day = 86400000;
19495 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19498 //Roo.log(prevStart);
19500 var today = new Date().clearTime().getTime();
19501 var sel = date.clearTime().getTime();
19502 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19503 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19504 var ddMatch = this.disabledDatesRE;
19505 var ddText = this.disabledDatesText;
19506 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19507 var ddaysText = this.disabledDaysText;
19508 var format = this.format;
19510 var setCellClass = function(cal, cell){
19514 //Roo.log('set Cell Class');
19516 var t = d.getTime();
19520 cell.dateValue = t;
19522 cell.className += " fc-today";
19523 cell.className += " fc-state-highlight";
19524 cell.title = cal.todayText;
19527 // disable highlight in other month..
19528 //cell.className += " fc-state-highlight";
19533 cell.className = " fc-state-disabled";
19534 cell.title = cal.minText;
19538 cell.className = " fc-state-disabled";
19539 cell.title = cal.maxText;
19543 if(ddays.indexOf(d.getDay()) != -1){
19544 cell.title = ddaysText;
19545 cell.className = " fc-state-disabled";
19548 if(ddMatch && format){
19549 var fvalue = d.dateFormat(format);
19550 if(ddMatch.test(fvalue)){
19551 cell.title = ddText.replace("%0", fvalue);
19552 cell.className = " fc-state-disabled";
19556 if (!cell.initialClassName) {
19557 cell.initialClassName = cell.dom.className;
19560 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19565 for(; i < startingPos; i++) {
19566 textEls[i].innerHTML = (++prevStart);
19567 d.setDate(d.getDate()+1);
19569 cells[i].className = "fc-past fc-other-month";
19570 setCellClass(this, cells[i]);
19575 for(; i < days; i++){
19576 intDay = i - startingPos + 1;
19577 textEls[i].innerHTML = (intDay);
19578 d.setDate(d.getDate()+1);
19580 cells[i].className = ''; // "x-date-active";
19581 setCellClass(this, cells[i]);
19585 for(; i < 42; i++) {
19586 textEls[i].innerHTML = (++extraDays);
19587 d.setDate(d.getDate()+1);
19589 cells[i].className = "fc-future fc-other-month";
19590 setCellClass(this, cells[i]);
19593 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19595 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19597 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19598 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19600 if(totalRows != 6){
19601 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19602 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19605 this.fireEvent('monthchange', this, date);
19609 if(!this.internalRender){
19610 var main = this.el.dom.firstChild;
19611 var w = main.offsetWidth;
19612 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19613 Roo.fly(main).setWidth(w);
19614 this.internalRender = true;
19615 // opera does not respect the auto grow header center column
19616 // then, after it gets a width opera refuses to recalculate
19617 // without a second pass
19618 if(Roo.isOpera && !this.secondPass){
19619 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19620 this.secondPass = true;
19621 this.update.defer(10, this, [date]);
19628 findCell : function(dt) {
19629 dt = dt.clearTime().getTime();
19631 this.cells.each(function(c){
19632 //Roo.log("check " +c.dateValue + '?=' + dt);
19633 if(c.dateValue == dt){
19643 findCells : function(ev) {
19644 var s = ev.start.clone().clearTime().getTime();
19646 var e= ev.end.clone().clearTime().getTime();
19649 this.cells.each(function(c){
19650 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19652 if(c.dateValue > e){
19655 if(c.dateValue < s){
19664 // findBestRow: function(cells)
19668 // for (var i =0 ; i < cells.length;i++) {
19669 // ret = Math.max(cells[i].rows || 0,ret);
19676 addItem : function(ev)
19678 // look for vertical location slot in
19679 var cells = this.findCells(ev);
19681 // ev.row = this.findBestRow(cells);
19683 // work out the location.
19687 for(var i =0; i < cells.length; i++) {
19689 cells[i].row = cells[0].row;
19692 cells[i].row = cells[i].row + 1;
19702 if (crow.start.getY() == cells[i].getY()) {
19704 crow.end = cells[i];
19721 cells[0].events.push(ev);
19723 this.calevents.push(ev);
19726 clearEvents: function() {
19728 if(!this.calevents){
19732 Roo.each(this.cells.elements, function(c){
19738 Roo.each(this.calevents, function(e) {
19739 Roo.each(e.els, function(el) {
19740 el.un('mouseenter' ,this.onEventEnter, this);
19741 el.un('mouseleave' ,this.onEventLeave, this);
19746 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19752 renderEvents: function()
19756 this.cells.each(function(c) {
19765 if(c.row != c.events.length){
19766 r = 4 - (4 - (c.row - c.events.length));
19769 c.events = ev.slice(0, r);
19770 c.more = ev.slice(r);
19772 if(c.more.length && c.more.length == 1){
19773 c.events.push(c.more.pop());
19776 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19780 this.cells.each(function(c) {
19782 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19785 for (var e = 0; e < c.events.length; e++){
19786 var ev = c.events[e];
19787 var rows = ev.rows;
19789 for(var i = 0; i < rows.length; i++) {
19791 // how many rows should it span..
19794 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19795 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19797 unselectable : "on",
19800 cls: 'fc-event-inner',
19804 // cls: 'fc-event-time',
19805 // html : cells.length > 1 ? '' : ev.time
19809 cls: 'fc-event-title',
19810 html : String.format('{0}', ev.title)
19817 cls: 'ui-resizable-handle ui-resizable-e',
19818 html : '  '
19825 cfg.cls += ' fc-event-start';
19827 if ((i+1) == rows.length) {
19828 cfg.cls += ' fc-event-end';
19831 var ctr = _this.el.select('.fc-event-container',true).first();
19832 var cg = ctr.createChild(cfg);
19834 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19835 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19837 var r = (c.more.length) ? 1 : 0;
19838 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19839 cg.setWidth(ebox.right - sbox.x -2);
19841 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19842 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19843 cg.on('click', _this.onEventClick, _this, ev);
19854 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19855 style : 'position: absolute',
19856 unselectable : "on",
19859 cls: 'fc-event-inner',
19863 cls: 'fc-event-title',
19871 cls: 'ui-resizable-handle ui-resizable-e',
19872 html : '  '
19878 var ctr = _this.el.select('.fc-event-container',true).first();
19879 var cg = ctr.createChild(cfg);
19881 var sbox = c.select('.fc-day-content',true).first().getBox();
19882 var ebox = c.select('.fc-day-content',true).first().getBox();
19884 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19885 cg.setWidth(ebox.right - sbox.x -2);
19887 cg.on('click', _this.onMoreEventClick, _this, c.more);
19897 onEventEnter: function (e, el,event,d) {
19898 this.fireEvent('evententer', this, el, event);
19901 onEventLeave: function (e, el,event,d) {
19902 this.fireEvent('eventleave', this, el, event);
19905 onEventClick: function (e, el,event,d) {
19906 this.fireEvent('eventclick', this, el, event);
19909 onMonthChange: function () {
19913 onMoreEventClick: function(e, el, more)
19917 this.calpopover.placement = 'right';
19918 this.calpopover.setTitle('More');
19920 this.calpopover.setContent('');
19922 var ctr = this.calpopover.el.select('.popover-content', true).first();
19924 Roo.each(more, function(m){
19926 cls : 'fc-event-hori fc-event-draggable',
19929 var cg = ctr.createChild(cfg);
19931 cg.on('click', _this.onEventClick, _this, m);
19934 this.calpopover.show(el);
19939 onLoad: function ()
19941 this.calevents = [];
19944 if(this.store.getCount() > 0){
19945 this.store.data.each(function(d){
19948 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19949 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19950 time : d.data.start_time,
19951 title : d.data.title,
19952 description : d.data.description,
19953 venue : d.data.venue
19958 this.renderEvents();
19960 if(this.calevents.length && this.loadMask){
19961 this.maskEl.hide();
19965 onBeforeLoad: function()
19967 this.clearEvents();
19969 this.maskEl.show();
19983 * @class Roo.bootstrap.Popover
19984 * @extends Roo.bootstrap.Component
19985 * Bootstrap Popover class
19986 * @cfg {String} html contents of the popover (or false to use children..)
19987 * @cfg {String} title of popover (or false to hide)
19988 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19989 * @cfg {String} trigger click || hover (or false to trigger manually)
19990 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19991 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19992 * - if false and it has a 'parent' then it will be automatically added to that element
19993 * - if string - Roo.get will be called
19994 * @cfg {Number} delay - delay before showing
19997 * Create a new Popover
19998 * @param {Object} config The config object
20001 Roo.bootstrap.Popover = function(config){
20002 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20008 * After the popover show
20010 * @param {Roo.bootstrap.Popover} this
20015 * After the popover hide
20017 * @param {Roo.bootstrap.Popover} this
20023 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20028 placement : 'right',
20029 trigger : 'hover', // hover
20035 can_build_overlaid : false,
20037 maskEl : false, // the mask element
20040 alignEl : false, // when show is called with an element - this get's stored.
20042 getChildContainer : function()
20044 return this.contentEl;
20047 getPopoverHeader : function()
20049 this.title = true; // flag not to hide it..
20050 this.headerEl.addClass('p-0');
20051 return this.headerEl
20055 getAutoCreate : function(){
20058 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20059 style: 'display:block',
20065 cls : 'popover-inner ',
20069 cls: 'popover-title popover-header',
20070 html : this.title === false ? '' : this.title
20073 cls : 'popover-content popover-body ' + (this.cls || ''),
20074 html : this.html || ''
20085 * @param {string} the title
20087 setTitle: function(str)
20091 this.headerEl.dom.innerHTML = str;
20096 * @param {string} the body content
20098 setContent: function(str)
20101 if (this.contentEl) {
20102 this.contentEl.dom.innerHTML = str;
20106 // as it get's added to the bottom of the page.
20107 onRender : function(ct, position)
20109 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20114 var cfg = Roo.apply({}, this.getAutoCreate());
20118 cfg.cls += ' ' + this.cls;
20121 cfg.style = this.style;
20123 //Roo.log("adding to ");
20124 this.el = Roo.get(document.body).createChild(cfg, position);
20125 // Roo.log(this.el);
20128 this.contentEl = this.el.select('.popover-content',true).first();
20129 this.headerEl = this.el.select('.popover-title',true).first();
20132 if(typeof(this.items) != 'undefined'){
20133 var items = this.items;
20136 for(var i =0;i < items.length;i++) {
20137 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20141 this.items = nitems;
20143 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20144 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20151 resizeMask : function()
20153 this.maskEl.setSize(
20154 Roo.lib.Dom.getViewWidth(true),
20155 Roo.lib.Dom.getViewHeight(true)
20159 initEvents : function()
20163 Roo.bootstrap.Popover.register(this);
20166 this.arrowEl = this.el.select('.arrow',true).first();
20167 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20168 this.el.enableDisplayMode('block');
20172 if (this.over === false && !this.parent()) {
20175 if (this.triggers === false) {
20180 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20181 var triggers = this.trigger ? this.trigger.split(' ') : [];
20182 Roo.each(triggers, function(trigger) {
20184 if (trigger == 'click') {
20185 on_el.on('click', this.toggle, this);
20186 } else if (trigger != 'manual') {
20187 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20188 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20190 on_el.on(eventIn ,this.enter, this);
20191 on_el.on(eventOut, this.leave, this);
20201 toggle : function () {
20202 this.hoverState == 'in' ? this.leave() : this.enter();
20205 enter : function () {
20207 clearTimeout(this.timeout);
20209 this.hoverState = 'in';
20211 if (!this.delay || !this.delay.show) {
20216 this.timeout = setTimeout(function () {
20217 if (_t.hoverState == 'in') {
20220 }, this.delay.show)
20223 leave : function() {
20224 clearTimeout(this.timeout);
20226 this.hoverState = 'out';
20228 if (!this.delay || !this.delay.hide) {
20233 this.timeout = setTimeout(function () {
20234 if (_t.hoverState == 'out') {
20237 }, this.delay.hide)
20241 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20242 * @param {string} (left|right|top|bottom) position
20244 show : function (on_el, placement)
20246 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20247 on_el = on_el || false; // default to false
20250 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20251 on_el = this.parent().el;
20252 } else if (this.over) {
20253 on_el = Roo.get(this.over);
20258 this.alignEl = Roo.get( on_el );
20261 this.render(document.body);
20267 if (this.title === false) {
20268 this.headerEl.hide();
20273 this.el.dom.style.display = 'block';
20276 if (this.alignEl) {
20277 this.updatePosition(this.placement, true);
20280 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20281 var es = this.el.getSize();
20282 var x = Roo.lib.Dom.getViewWidth()/2;
20283 var y = Roo.lib.Dom.getViewHeight()/2;
20284 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20289 //var arrow = this.el.select('.arrow',true).first();
20290 //arrow.set(align[2],
20292 this.el.addClass('in');
20296 this.hoverState = 'in';
20299 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20300 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20301 this.maskEl.dom.style.display = 'block';
20302 this.maskEl.addClass('show');
20304 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20306 this.fireEvent('show', this);
20310 * fire this manually after loading a grid in the table for example
20311 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20312 * @param {Boolean} try and move it if we cant get right position.
20314 updatePosition : function(placement, try_move)
20316 // allow for calling with no parameters
20317 placement = placement ? placement : this.placement;
20318 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20320 this.el.removeClass([
20321 'fade','top','bottom', 'left', 'right','in',
20322 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20324 this.el.addClass(placement + ' bs-popover-' + placement);
20326 if (!this.alignEl ) {
20330 switch (placement) {
20332 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20333 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20334 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20335 //normal display... or moved up/down.
20336 this.el.setXY(offset);
20337 var xy = this.alignEl.getAnchorXY('tr', false);
20339 this.arrowEl.setXY(xy);
20342 // continue through...
20343 return this.updatePosition('left', false);
20347 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20348 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20349 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20350 //normal display... or moved up/down.
20351 this.el.setXY(offset);
20352 var xy = this.alignEl.getAnchorXY('tl', false);
20353 xy[0]-=10;xy[1]+=5; // << fix me
20354 this.arrowEl.setXY(xy);
20358 return this.updatePosition('right', false);
20361 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20362 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20363 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20364 //normal display... or moved up/down.
20365 this.el.setXY(offset);
20366 var xy = this.alignEl.getAnchorXY('t', false);
20367 xy[1]-=10; // << fix me
20368 this.arrowEl.setXY(xy);
20372 return this.updatePosition('bottom', false);
20375 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20376 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20377 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20378 //normal display... or moved up/down.
20379 this.el.setXY(offset);
20380 var xy = this.alignEl.getAnchorXY('b', false);
20381 xy[1]+=2; // << fix me
20382 this.arrowEl.setXY(xy);
20386 return this.updatePosition('top', false);
20397 this.el.setXY([0,0]);
20398 this.el.removeClass('in');
20400 this.hoverState = null;
20401 this.maskEl.hide(); // always..
20402 this.fireEvent('hide', this);
20408 Roo.apply(Roo.bootstrap.Popover, {
20411 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20412 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20413 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20414 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20419 clickHander : false,
20423 onMouseDown : function(e)
20425 if (this.popups.length && !e.getTarget(".roo-popover")) {
20426 /// what is nothing is showing..
20435 register : function(popup)
20437 if (!Roo.bootstrap.Popover.clickHandler) {
20438 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20440 // hide other popups.
20441 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
20442 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
20443 this.hideAll(); //<< why?
20444 //this.popups.push(popup);
20446 hideAll : function()
20448 this.popups.forEach(function(p) {
20452 onShow : function() {
20453 Roo.bootstrap.Popover.popups.push(this);
20455 onHide : function() {
20456 Roo.bootstrap.Popover.popups.remove(this);
20462 * Card header - holder for the card header elements.
20467 * @class Roo.bootstrap.PopoverNav
20468 * @extends Roo.bootstrap.NavGroup
20469 * Bootstrap Popover header navigation class
20471 * Create a new Popover Header Navigation
20472 * @param {Object} config The config object
20475 Roo.bootstrap.PopoverNav = function(config){
20476 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20479 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20482 container_method : 'getPopoverHeader'
20500 * @class Roo.bootstrap.Progress
20501 * @extends Roo.bootstrap.Component
20502 * Bootstrap Progress class
20503 * @cfg {Boolean} striped striped of the progress bar
20504 * @cfg {Boolean} active animated of the progress bar
20508 * Create a new Progress
20509 * @param {Object} config The config object
20512 Roo.bootstrap.Progress = function(config){
20513 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20516 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20521 getAutoCreate : function(){
20529 cfg.cls += ' progress-striped';
20533 cfg.cls += ' active';
20552 * @class Roo.bootstrap.ProgressBar
20553 * @extends Roo.bootstrap.Component
20554 * Bootstrap ProgressBar class
20555 * @cfg {Number} aria_valuenow aria-value now
20556 * @cfg {Number} aria_valuemin aria-value min
20557 * @cfg {Number} aria_valuemax aria-value max
20558 * @cfg {String} label label for the progress bar
20559 * @cfg {String} panel (success | info | warning | danger )
20560 * @cfg {String} role role of the progress bar
20561 * @cfg {String} sr_only text
20565 * Create a new ProgressBar
20566 * @param {Object} config The config object
20569 Roo.bootstrap.ProgressBar = function(config){
20570 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20573 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20577 aria_valuemax : 100,
20583 getAutoCreate : function()
20588 cls: 'progress-bar',
20589 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20601 cfg.role = this.role;
20604 if(this.aria_valuenow){
20605 cfg['aria-valuenow'] = this.aria_valuenow;
20608 if(this.aria_valuemin){
20609 cfg['aria-valuemin'] = this.aria_valuemin;
20612 if(this.aria_valuemax){
20613 cfg['aria-valuemax'] = this.aria_valuemax;
20616 if(this.label && !this.sr_only){
20617 cfg.html = this.label;
20621 cfg.cls += ' progress-bar-' + this.panel;
20627 update : function(aria_valuenow)
20629 this.aria_valuenow = aria_valuenow;
20631 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20646 * @class Roo.bootstrap.TabGroup
20647 * @extends Roo.bootstrap.Column
20648 * Bootstrap Column class
20649 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20650 * @cfg {Boolean} carousel true to make the group behave like a carousel
20651 * @cfg {Boolean} bullets show bullets for the panels
20652 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20653 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20654 * @cfg {Boolean} showarrow (true|false) show arrow default true
20657 * Create a new TabGroup
20658 * @param {Object} config The config object
20661 Roo.bootstrap.TabGroup = function(config){
20662 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20664 this.navId = Roo.id();
20667 Roo.bootstrap.TabGroup.register(this);
20671 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20674 transition : false,
20679 slideOnTouch : false,
20682 getAutoCreate : function()
20684 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20686 cfg.cls += ' tab-content';
20688 if (this.carousel) {
20689 cfg.cls += ' carousel slide';
20692 cls : 'carousel-inner',
20696 if(this.bullets && !Roo.isTouch){
20699 cls : 'carousel-bullets',
20703 if(this.bullets_cls){
20704 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20711 cfg.cn[0].cn.push(bullets);
20714 if(this.showarrow){
20715 cfg.cn[0].cn.push({
20717 class : 'carousel-arrow',
20721 class : 'carousel-prev',
20725 class : 'fa fa-chevron-left'
20731 class : 'carousel-next',
20735 class : 'fa fa-chevron-right'
20748 initEvents: function()
20750 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20751 // this.el.on("touchstart", this.onTouchStart, this);
20754 if(this.autoslide){
20757 this.slideFn = window.setInterval(function() {
20758 _this.showPanelNext();
20762 if(this.showarrow){
20763 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20764 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20770 // onTouchStart : function(e, el, o)
20772 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20776 // this.showPanelNext();
20780 getChildContainer : function()
20782 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20786 * register a Navigation item
20787 * @param {Roo.bootstrap.NavItem} the navitem to add
20789 register : function(item)
20791 this.tabs.push( item);
20792 item.navId = this.navId; // not really needed..
20797 getActivePanel : function()
20800 Roo.each(this.tabs, function(t) {
20810 getPanelByName : function(n)
20813 Roo.each(this.tabs, function(t) {
20814 if (t.tabId == n) {
20822 indexOfPanel : function(p)
20825 Roo.each(this.tabs, function(t,i) {
20826 if (t.tabId == p.tabId) {
20835 * show a specific panel
20836 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20837 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20839 showPanel : function (pan)
20841 if(this.transition || typeof(pan) == 'undefined'){
20842 Roo.log("waiting for the transitionend");
20846 if (typeof(pan) == 'number') {
20847 pan = this.tabs[pan];
20850 if (typeof(pan) == 'string') {
20851 pan = this.getPanelByName(pan);
20854 var cur = this.getActivePanel();
20857 Roo.log('pan or acitve pan is undefined');
20861 if (pan.tabId == this.getActivePanel().tabId) {
20865 if (false === cur.fireEvent('beforedeactivate')) {
20869 if(this.bullets > 0 && !Roo.isTouch){
20870 this.setActiveBullet(this.indexOfPanel(pan));
20873 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20875 //class="carousel-item carousel-item-next carousel-item-left"
20877 this.transition = true;
20878 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20879 var lr = dir == 'next' ? 'left' : 'right';
20880 pan.el.addClass(dir); // or prev
20881 pan.el.addClass('carousel-item-' + dir); // or prev
20882 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20883 cur.el.addClass(lr); // or right
20884 pan.el.addClass(lr);
20885 cur.el.addClass('carousel-item-' +lr); // or right
20886 pan.el.addClass('carousel-item-' +lr);
20890 cur.el.on('transitionend', function() {
20891 Roo.log("trans end?");
20893 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20894 pan.setActive(true);
20896 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20897 cur.setActive(false);
20899 _this.transition = false;
20901 }, this, { single: true } );
20906 cur.setActive(false);
20907 pan.setActive(true);
20912 showPanelNext : function()
20914 var i = this.indexOfPanel(this.getActivePanel());
20916 if (i >= this.tabs.length - 1 && !this.autoslide) {
20920 if (i >= this.tabs.length - 1 && this.autoslide) {
20924 this.showPanel(this.tabs[i+1]);
20927 showPanelPrev : function()
20929 var i = this.indexOfPanel(this.getActivePanel());
20931 if (i < 1 && !this.autoslide) {
20935 if (i < 1 && this.autoslide) {
20936 i = this.tabs.length;
20939 this.showPanel(this.tabs[i-1]);
20943 addBullet: function()
20945 if(!this.bullets || Roo.isTouch){
20948 var ctr = this.el.select('.carousel-bullets',true).first();
20949 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20950 var bullet = ctr.createChild({
20951 cls : 'bullet bullet-' + i
20952 },ctr.dom.lastChild);
20957 bullet.on('click', (function(e, el, o, ii, t){
20959 e.preventDefault();
20961 this.showPanel(ii);
20963 if(this.autoslide && this.slideFn){
20964 clearInterval(this.slideFn);
20965 this.slideFn = window.setInterval(function() {
20966 _this.showPanelNext();
20970 }).createDelegate(this, [i, bullet], true));
20975 setActiveBullet : function(i)
20981 Roo.each(this.el.select('.bullet', true).elements, function(el){
20982 el.removeClass('selected');
20985 var bullet = this.el.select('.bullet-' + i, true).first();
20991 bullet.addClass('selected');
21002 Roo.apply(Roo.bootstrap.TabGroup, {
21006 * register a Navigation Group
21007 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21009 register : function(navgrp)
21011 this.groups[navgrp.navId] = navgrp;
21015 * fetch a Navigation Group based on the navigation ID
21016 * if one does not exist , it will get created.
21017 * @param {string} the navgroup to add
21018 * @returns {Roo.bootstrap.NavGroup} the navgroup
21020 get: function(navId) {
21021 if (typeof(this.groups[navId]) == 'undefined') {
21022 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21024 return this.groups[navId] ;
21039 * @class Roo.bootstrap.TabPanel
21040 * @extends Roo.bootstrap.Component
21041 * Bootstrap TabPanel class
21042 * @cfg {Boolean} active panel active
21043 * @cfg {String} html panel content
21044 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21045 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21046 * @cfg {String} href click to link..
21047 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21051 * Create a new TabPanel
21052 * @param {Object} config The config object
21055 Roo.bootstrap.TabPanel = function(config){
21056 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21060 * Fires when the active status changes
21061 * @param {Roo.bootstrap.TabPanel} this
21062 * @param {Boolean} state the new state
21067 * @event beforedeactivate
21068 * Fires before a tab is de-activated - can be used to do validation on a form.
21069 * @param {Roo.bootstrap.TabPanel} this
21070 * @return {Boolean} false if there is an error
21073 'beforedeactivate': true
21076 this.tabId = this.tabId || Roo.id();
21080 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21087 touchSlide : false,
21088 getAutoCreate : function(){
21093 // item is needed for carousel - not sure if it has any effect otherwise
21094 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21095 html: this.html || ''
21099 cfg.cls += ' active';
21103 cfg.tabId = this.tabId;
21111 initEvents: function()
21113 var p = this.parent();
21115 this.navId = this.navId || p.navId;
21117 if (typeof(this.navId) != 'undefined') {
21118 // not really needed.. but just in case.. parent should be a NavGroup.
21119 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21123 var i = tg.tabs.length - 1;
21125 if(this.active && tg.bullets > 0 && i < tg.bullets){
21126 tg.setActiveBullet(i);
21130 this.el.on('click', this.onClick, this);
21132 if(Roo.isTouch && this.touchSlide){
21133 this.el.on("touchstart", this.onTouchStart, this);
21134 this.el.on("touchmove", this.onTouchMove, this);
21135 this.el.on("touchend", this.onTouchEnd, this);
21140 onRender : function(ct, position)
21142 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21145 setActive : function(state)
21147 Roo.log("panel - set active " + this.tabId + "=" + state);
21149 this.active = state;
21151 this.el.removeClass('active');
21153 } else if (!this.el.hasClass('active')) {
21154 this.el.addClass('active');
21157 this.fireEvent('changed', this, state);
21160 onClick : function(e)
21162 e.preventDefault();
21164 if(!this.href.length){
21168 window.location.href = this.href;
21177 onTouchStart : function(e)
21179 this.swiping = false;
21181 this.startX = e.browserEvent.touches[0].clientX;
21182 this.startY = e.browserEvent.touches[0].clientY;
21185 onTouchMove : function(e)
21187 this.swiping = true;
21189 this.endX = e.browserEvent.touches[0].clientX;
21190 this.endY = e.browserEvent.touches[0].clientY;
21193 onTouchEnd : function(e)
21200 var tabGroup = this.parent();
21202 if(this.endX > this.startX){ // swiping right
21203 tabGroup.showPanelPrev();
21207 if(this.startX > this.endX){ // swiping left
21208 tabGroup.showPanelNext();
21227 * @class Roo.bootstrap.DateField
21228 * @extends Roo.bootstrap.Input
21229 * Bootstrap DateField class
21230 * @cfg {Number} weekStart default 0
21231 * @cfg {String} viewMode default empty, (months|years)
21232 * @cfg {String} minViewMode default empty, (months|years)
21233 * @cfg {Number} startDate default -Infinity
21234 * @cfg {Number} endDate default Infinity
21235 * @cfg {Boolean} todayHighlight default false
21236 * @cfg {Boolean} todayBtn default false
21237 * @cfg {Boolean} calendarWeeks default false
21238 * @cfg {Object} daysOfWeekDisabled default empty
21239 * @cfg {Boolean} singleMode default false (true | false)
21241 * @cfg {Boolean} keyboardNavigation default true
21242 * @cfg {String} language default en
21245 * Create a new DateField
21246 * @param {Object} config The config object
21249 Roo.bootstrap.DateField = function(config){
21250 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21254 * Fires when this field show.
21255 * @param {Roo.bootstrap.DateField} this
21256 * @param {Mixed} date The date value
21261 * Fires when this field hide.
21262 * @param {Roo.bootstrap.DateField} this
21263 * @param {Mixed} date The date value
21268 * Fires when select a date.
21269 * @param {Roo.bootstrap.DateField} this
21270 * @param {Mixed} date The date value
21274 * @event beforeselect
21275 * Fires when before select a date.
21276 * @param {Roo.bootstrap.DateField} this
21277 * @param {Mixed} date The date value
21279 beforeselect : true
21283 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21286 * @cfg {String} format
21287 * The default date format string which can be overriden for localization support. The format must be
21288 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21292 * @cfg {String} altFormats
21293 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21294 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21296 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21304 todayHighlight : false,
21310 keyboardNavigation: true,
21312 calendarWeeks: false,
21314 startDate: -Infinity,
21318 daysOfWeekDisabled: [],
21322 singleMode : false,
21324 UTCDate: function()
21326 return new Date(Date.UTC.apply(Date, arguments));
21329 UTCToday: function()
21331 var today = new Date();
21332 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21335 getDate: function() {
21336 var d = this.getUTCDate();
21337 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21340 getUTCDate: function() {
21344 setDate: function(d) {
21345 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21348 setUTCDate: function(d) {
21350 this.setValue(this.formatDate(this.date));
21353 onRender: function(ct, position)
21356 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21358 this.language = this.language || 'en';
21359 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21360 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21362 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21363 this.format = this.format || 'm/d/y';
21364 this.isInline = false;
21365 this.isInput = true;
21366 this.component = this.el.select('.add-on', true).first() || false;
21367 this.component = (this.component && this.component.length === 0) ? false : this.component;
21368 this.hasInput = this.component && this.inputEl().length;
21370 if (typeof(this.minViewMode === 'string')) {
21371 switch (this.minViewMode) {
21373 this.minViewMode = 1;
21376 this.minViewMode = 2;
21379 this.minViewMode = 0;
21384 if (typeof(this.viewMode === 'string')) {
21385 switch (this.viewMode) {
21398 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21400 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21402 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21404 this.picker().on('mousedown', this.onMousedown, this);
21405 this.picker().on('click', this.onClick, this);
21407 this.picker().addClass('datepicker-dropdown');
21409 this.startViewMode = this.viewMode;
21411 if(this.singleMode){
21412 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21413 v.setVisibilityMode(Roo.Element.DISPLAY);
21417 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21418 v.setStyle('width', '189px');
21422 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21423 if(!this.calendarWeeks){
21428 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21429 v.attr('colspan', function(i, val){
21430 return parseInt(val) + 1;
21435 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21437 this.setStartDate(this.startDate);
21438 this.setEndDate(this.endDate);
21440 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21447 if(this.isInline) {
21452 picker : function()
21454 return this.pickerEl;
21455 // return this.el.select('.datepicker', true).first();
21458 fillDow: function()
21460 var dowCnt = this.weekStart;
21469 if(this.calendarWeeks){
21477 while (dowCnt < this.weekStart + 7) {
21481 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21485 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21488 fillMonths: function()
21491 var months = this.picker().select('>.datepicker-months td', true).first();
21493 months.dom.innerHTML = '';
21499 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21502 months.createChild(month);
21509 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;
21511 if (this.date < this.startDate) {
21512 this.viewDate = new Date(this.startDate);
21513 } else if (this.date > this.endDate) {
21514 this.viewDate = new Date(this.endDate);
21516 this.viewDate = new Date(this.date);
21524 var d = new Date(this.viewDate),
21525 year = d.getUTCFullYear(),
21526 month = d.getUTCMonth(),
21527 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21528 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21529 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21530 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21531 currentDate = this.date && this.date.valueOf(),
21532 today = this.UTCToday();
21534 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21536 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21538 // this.picker.select('>tfoot th.today').
21539 // .text(dates[this.language].today)
21540 // .toggle(this.todayBtn !== false);
21542 this.updateNavArrows();
21545 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21547 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21549 prevMonth.setUTCDate(day);
21551 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21553 var nextMonth = new Date(prevMonth);
21555 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21557 nextMonth = nextMonth.valueOf();
21559 var fillMonths = false;
21561 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21563 while(prevMonth.valueOf() <= nextMonth) {
21566 if (prevMonth.getUTCDay() === this.weekStart) {
21568 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21576 if(this.calendarWeeks){
21577 // ISO 8601: First week contains first thursday.
21578 // ISO also states week starts on Monday, but we can be more abstract here.
21580 // Start of current week: based on weekstart/current date
21581 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21582 // Thursday of this week
21583 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21584 // First Thursday of year, year from thursday
21585 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21586 // Calendar week: ms between thursdays, div ms per day, div 7 days
21587 calWeek = (th - yth) / 864e5 / 7 + 1;
21589 fillMonths.cn.push({
21597 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21599 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21602 if (this.todayHighlight &&
21603 prevMonth.getUTCFullYear() == today.getFullYear() &&
21604 prevMonth.getUTCMonth() == today.getMonth() &&
21605 prevMonth.getUTCDate() == today.getDate()) {
21606 clsName += ' today';
21609 if (currentDate && prevMonth.valueOf() === currentDate) {
21610 clsName += ' active';
21613 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21614 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21615 clsName += ' disabled';
21618 fillMonths.cn.push({
21620 cls: 'day ' + clsName,
21621 html: prevMonth.getDate()
21624 prevMonth.setDate(prevMonth.getDate()+1);
21627 var currentYear = this.date && this.date.getUTCFullYear();
21628 var currentMonth = this.date && this.date.getUTCMonth();
21630 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21632 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21633 v.removeClass('active');
21635 if(currentYear === year && k === currentMonth){
21636 v.addClass('active');
21639 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21640 v.addClass('disabled');
21646 year = parseInt(year/10, 10) * 10;
21648 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21650 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21653 for (var i = -1; i < 11; i++) {
21654 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21656 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21664 showMode: function(dir)
21667 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21670 Roo.each(this.picker().select('>div',true).elements, function(v){
21671 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21674 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21679 if(this.isInline) {
21683 this.picker().removeClass(['bottom', 'top']);
21685 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21687 * place to the top of element!
21691 this.picker().addClass('top');
21692 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21697 this.picker().addClass('bottom');
21699 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21702 parseDate : function(value)
21704 if(!value || value instanceof Date){
21707 var v = Date.parseDate(value, this.format);
21708 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21709 v = Date.parseDate(value, 'Y-m-d');
21711 if(!v && this.altFormats){
21712 if(!this.altFormatsArray){
21713 this.altFormatsArray = this.altFormats.split("|");
21715 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21716 v = Date.parseDate(value, this.altFormatsArray[i]);
21722 formatDate : function(date, fmt)
21724 return (!date || !(date instanceof Date)) ?
21725 date : date.dateFormat(fmt || this.format);
21728 onFocus : function()
21730 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21734 onBlur : function()
21736 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21738 var d = this.inputEl().getValue();
21745 showPopup : function()
21747 this.picker().show();
21751 this.fireEvent('showpopup', this, this.date);
21754 hidePopup : function()
21756 if(this.isInline) {
21759 this.picker().hide();
21760 this.viewMode = this.startViewMode;
21763 this.fireEvent('hidepopup', this, this.date);
21767 onMousedown: function(e)
21769 e.stopPropagation();
21770 e.preventDefault();
21775 Roo.bootstrap.DateField.superclass.keyup.call(this);
21779 setValue: function(v)
21781 if(this.fireEvent('beforeselect', this, v) !== false){
21782 var d = new Date(this.parseDate(v) ).clearTime();
21784 if(isNaN(d.getTime())){
21785 this.date = this.viewDate = '';
21786 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21790 v = this.formatDate(d);
21792 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21794 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21798 this.fireEvent('select', this, this.date);
21802 getValue: function()
21804 return this.formatDate(this.date);
21807 fireKey: function(e)
21809 if (!this.picker().isVisible()){
21810 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21816 var dateChanged = false,
21818 newDate, newViewDate;
21823 e.preventDefault();
21827 if (!this.keyboardNavigation) {
21830 dir = e.keyCode == 37 ? -1 : 1;
21833 newDate = this.moveYear(this.date, dir);
21834 newViewDate = this.moveYear(this.viewDate, dir);
21835 } else if (e.shiftKey){
21836 newDate = this.moveMonth(this.date, dir);
21837 newViewDate = this.moveMonth(this.viewDate, dir);
21839 newDate = new Date(this.date);
21840 newDate.setUTCDate(this.date.getUTCDate() + dir);
21841 newViewDate = new Date(this.viewDate);
21842 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21844 if (this.dateWithinRange(newDate)){
21845 this.date = newDate;
21846 this.viewDate = newViewDate;
21847 this.setValue(this.formatDate(this.date));
21849 e.preventDefault();
21850 dateChanged = true;
21855 if (!this.keyboardNavigation) {
21858 dir = e.keyCode == 38 ? -1 : 1;
21860 newDate = this.moveYear(this.date, dir);
21861 newViewDate = this.moveYear(this.viewDate, dir);
21862 } else if (e.shiftKey){
21863 newDate = this.moveMonth(this.date, dir);
21864 newViewDate = this.moveMonth(this.viewDate, dir);
21866 newDate = new Date(this.date);
21867 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21868 newViewDate = new Date(this.viewDate);
21869 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21871 if (this.dateWithinRange(newDate)){
21872 this.date = newDate;
21873 this.viewDate = newViewDate;
21874 this.setValue(this.formatDate(this.date));
21876 e.preventDefault();
21877 dateChanged = true;
21881 this.setValue(this.formatDate(this.date));
21883 e.preventDefault();
21886 this.setValue(this.formatDate(this.date));
21900 onClick: function(e)
21902 e.stopPropagation();
21903 e.preventDefault();
21905 var target = e.getTarget();
21907 if(target.nodeName.toLowerCase() === 'i'){
21908 target = Roo.get(target).dom.parentNode;
21911 var nodeName = target.nodeName;
21912 var className = target.className;
21913 var html = target.innerHTML;
21914 //Roo.log(nodeName);
21916 switch(nodeName.toLowerCase()) {
21918 switch(className) {
21924 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21925 switch(this.viewMode){
21927 this.viewDate = this.moveMonth(this.viewDate, dir);
21931 this.viewDate = this.moveYear(this.viewDate, dir);
21937 var date = new Date();
21938 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21940 this.setValue(this.formatDate(this.date));
21947 if (className.indexOf('disabled') < 0) {
21948 this.viewDate.setUTCDate(1);
21949 if (className.indexOf('month') > -1) {
21950 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21952 var year = parseInt(html, 10) || 0;
21953 this.viewDate.setUTCFullYear(year);
21957 if(this.singleMode){
21958 this.setValue(this.formatDate(this.viewDate));
21969 //Roo.log(className);
21970 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21971 var day = parseInt(html, 10) || 1;
21972 var year = (this.viewDate || new Date()).getUTCFullYear(),
21973 month = (this.viewDate || new Date()).getUTCMonth();
21975 if (className.indexOf('old') > -1) {
21982 } else if (className.indexOf('new') > -1) {
21990 //Roo.log([year,month,day]);
21991 this.date = this.UTCDate(year, month, day,0,0,0,0);
21992 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21994 //Roo.log(this.formatDate(this.date));
21995 this.setValue(this.formatDate(this.date));
22002 setStartDate: function(startDate)
22004 this.startDate = startDate || -Infinity;
22005 if (this.startDate !== -Infinity) {
22006 this.startDate = this.parseDate(this.startDate);
22009 this.updateNavArrows();
22012 setEndDate: function(endDate)
22014 this.endDate = endDate || Infinity;
22015 if (this.endDate !== Infinity) {
22016 this.endDate = this.parseDate(this.endDate);
22019 this.updateNavArrows();
22022 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22024 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22025 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22026 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22028 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22029 return parseInt(d, 10);
22032 this.updateNavArrows();
22035 updateNavArrows: function()
22037 if(this.singleMode){
22041 var d = new Date(this.viewDate),
22042 year = d.getUTCFullYear(),
22043 month = d.getUTCMonth();
22045 Roo.each(this.picker().select('.prev', true).elements, function(v){
22047 switch (this.viewMode) {
22050 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22056 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22063 Roo.each(this.picker().select('.next', true).elements, function(v){
22065 switch (this.viewMode) {
22068 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22074 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22082 moveMonth: function(date, dir)
22087 var new_date = new Date(date.valueOf()),
22088 day = new_date.getUTCDate(),
22089 month = new_date.getUTCMonth(),
22090 mag = Math.abs(dir),
22092 dir = dir > 0 ? 1 : -1;
22095 // If going back one month, make sure month is not current month
22096 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22098 return new_date.getUTCMonth() == month;
22100 // If going forward one month, make sure month is as expected
22101 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22103 return new_date.getUTCMonth() != new_month;
22105 new_month = month + dir;
22106 new_date.setUTCMonth(new_month);
22107 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22108 if (new_month < 0 || new_month > 11) {
22109 new_month = (new_month + 12) % 12;
22112 // For magnitudes >1, move one month at a time...
22113 for (var i=0; i<mag; i++) {
22114 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22115 new_date = this.moveMonth(new_date, dir);
22117 // ...then reset the day, keeping it in the new month
22118 new_month = new_date.getUTCMonth();
22119 new_date.setUTCDate(day);
22121 return new_month != new_date.getUTCMonth();
22124 // Common date-resetting loop -- if date is beyond end of month, make it
22127 new_date.setUTCDate(--day);
22128 new_date.setUTCMonth(new_month);
22133 moveYear: function(date, dir)
22135 return this.moveMonth(date, dir*12);
22138 dateWithinRange: function(date)
22140 return date >= this.startDate && date <= this.endDate;
22146 this.picker().remove();
22149 validateValue : function(value)
22151 if(this.getVisibilityEl().hasClass('hidden')){
22155 if(value.length < 1) {
22156 if(this.allowBlank){
22162 if(value.length < this.minLength){
22165 if(value.length > this.maxLength){
22169 var vt = Roo.form.VTypes;
22170 if(!vt[this.vtype](value, this)){
22174 if(typeof this.validator == "function"){
22175 var msg = this.validator(value);
22181 if(this.regex && !this.regex.test(value)){
22185 if(typeof(this.parseDate(value)) == 'undefined'){
22189 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22193 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22203 this.date = this.viewDate = '';
22205 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22210 Roo.apply(Roo.bootstrap.DateField, {
22221 html: '<i class="fa fa-arrow-left"/>'
22231 html: '<i class="fa fa-arrow-right"/>'
22273 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22274 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22275 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22276 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22277 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22290 navFnc: 'FullYear',
22295 navFnc: 'FullYear',
22300 Roo.apply(Roo.bootstrap.DateField, {
22304 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22308 cls: 'datepicker-days',
22312 cls: 'table-condensed',
22314 Roo.bootstrap.DateField.head,
22318 Roo.bootstrap.DateField.footer
22325 cls: 'datepicker-months',
22329 cls: 'table-condensed',
22331 Roo.bootstrap.DateField.head,
22332 Roo.bootstrap.DateField.content,
22333 Roo.bootstrap.DateField.footer
22340 cls: 'datepicker-years',
22344 cls: 'table-condensed',
22346 Roo.bootstrap.DateField.head,
22347 Roo.bootstrap.DateField.content,
22348 Roo.bootstrap.DateField.footer
22367 * @class Roo.bootstrap.TimeField
22368 * @extends Roo.bootstrap.Input
22369 * Bootstrap DateField class
22373 * Create a new TimeField
22374 * @param {Object} config The config object
22377 Roo.bootstrap.TimeField = function(config){
22378 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22382 * Fires when this field show.
22383 * @param {Roo.bootstrap.DateField} thisthis
22384 * @param {Mixed} date The date value
22389 * Fires when this field hide.
22390 * @param {Roo.bootstrap.DateField} this
22391 * @param {Mixed} date The date value
22396 * Fires when select a date.
22397 * @param {Roo.bootstrap.DateField} this
22398 * @param {Mixed} date The date value
22404 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22407 * @cfg {String} format
22408 * The default time format string which can be overriden for localization support. The format must be
22409 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22413 getAutoCreate : function()
22415 this.after = '<i class="fa far fa-clock"></i>';
22416 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22420 onRender: function(ct, position)
22423 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22425 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22427 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22429 this.pop = this.picker().select('>.datepicker-time',true).first();
22430 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22432 this.picker().on('mousedown', this.onMousedown, this);
22433 this.picker().on('click', this.onClick, this);
22435 this.picker().addClass('datepicker-dropdown');
22440 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22441 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22442 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22443 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22444 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22445 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22449 fireKey: function(e){
22450 if (!this.picker().isVisible()){
22451 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22457 e.preventDefault();
22465 this.onTogglePeriod();
22468 this.onIncrementMinutes();
22471 this.onDecrementMinutes();
22480 onClick: function(e) {
22481 e.stopPropagation();
22482 e.preventDefault();
22485 picker : function()
22487 return this.pickerEl;
22490 fillTime: function()
22492 var time = this.pop.select('tbody', true).first();
22494 time.dom.innerHTML = '';
22509 cls: 'hours-up fa fas fa-chevron-up'
22529 cls: 'minutes-up fa fas fa-chevron-up'
22550 cls: 'timepicker-hour',
22565 cls: 'timepicker-minute',
22580 cls: 'btn btn-primary period',
22602 cls: 'hours-down fa fas fa-chevron-down'
22622 cls: 'minutes-down fa fas fa-chevron-down'
22640 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22647 var hours = this.time.getHours();
22648 var minutes = this.time.getMinutes();
22661 hours = hours - 12;
22665 hours = '0' + hours;
22669 minutes = '0' + minutes;
22672 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22673 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22674 this.pop.select('button', true).first().dom.innerHTML = period;
22680 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22682 var cls = ['bottom'];
22684 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22691 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22695 //this.picker().setXY(20000,20000);
22696 this.picker().addClass(cls.join('-'));
22700 Roo.each(cls, function(c){
22705 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22706 //_this.picker().setTop(_this.inputEl().getHeight());
22710 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22712 //_this.picker().setTop(0 - _this.picker().getHeight());
22717 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22721 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22729 onFocus : function()
22731 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22735 onBlur : function()
22737 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22743 this.picker().show();
22748 this.fireEvent('show', this, this.date);
22753 this.picker().hide();
22756 this.fireEvent('hide', this, this.date);
22759 setTime : function()
22762 this.setValue(this.time.format(this.format));
22764 this.fireEvent('select', this, this.date);
22769 onMousedown: function(e){
22770 e.stopPropagation();
22771 e.preventDefault();
22774 onIncrementHours: function()
22776 Roo.log('onIncrementHours');
22777 this.time = this.time.add(Date.HOUR, 1);
22782 onDecrementHours: function()
22784 Roo.log('onDecrementHours');
22785 this.time = this.time.add(Date.HOUR, -1);
22789 onIncrementMinutes: function()
22791 Roo.log('onIncrementMinutes');
22792 this.time = this.time.add(Date.MINUTE, 1);
22796 onDecrementMinutes: function()
22798 Roo.log('onDecrementMinutes');
22799 this.time = this.time.add(Date.MINUTE, -1);
22803 onTogglePeriod: function()
22805 Roo.log('onTogglePeriod');
22806 this.time = this.time.add(Date.HOUR, 12);
22814 Roo.apply(Roo.bootstrap.TimeField, {
22818 cls: 'datepicker dropdown-menu',
22822 cls: 'datepicker-time',
22826 cls: 'table-condensed',
22855 cls: 'btn btn-info ok',
22883 * @class Roo.bootstrap.MonthField
22884 * @extends Roo.bootstrap.Input
22885 * Bootstrap MonthField class
22887 * @cfg {String} language default en
22890 * Create a new MonthField
22891 * @param {Object} config The config object
22894 Roo.bootstrap.MonthField = function(config){
22895 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22900 * Fires when this field show.
22901 * @param {Roo.bootstrap.MonthField} this
22902 * @param {Mixed} date The date value
22907 * Fires when this field hide.
22908 * @param {Roo.bootstrap.MonthField} this
22909 * @param {Mixed} date The date value
22914 * Fires when select a date.
22915 * @param {Roo.bootstrap.MonthField} this
22916 * @param {String} oldvalue The old value
22917 * @param {String} newvalue The new value
22923 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22925 onRender: function(ct, position)
22928 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22930 this.language = this.language || 'en';
22931 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22932 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22934 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22935 this.isInline = false;
22936 this.isInput = true;
22937 this.component = this.el.select('.add-on', true).first() || false;
22938 this.component = (this.component && this.component.length === 0) ? false : this.component;
22939 this.hasInput = this.component && this.inputEL().length;
22941 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22943 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22945 this.picker().on('mousedown', this.onMousedown, this);
22946 this.picker().on('click', this.onClick, this);
22948 this.picker().addClass('datepicker-dropdown');
22950 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22951 v.setStyle('width', '189px');
22958 if(this.isInline) {
22964 setValue: function(v, suppressEvent)
22966 var o = this.getValue();
22968 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22972 if(suppressEvent !== true){
22973 this.fireEvent('select', this, o, v);
22978 getValue: function()
22983 onClick: function(e)
22985 e.stopPropagation();
22986 e.preventDefault();
22988 var target = e.getTarget();
22990 if(target.nodeName.toLowerCase() === 'i'){
22991 target = Roo.get(target).dom.parentNode;
22994 var nodeName = target.nodeName;
22995 var className = target.className;
22996 var html = target.innerHTML;
22998 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23002 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23004 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23010 picker : function()
23012 return this.pickerEl;
23015 fillMonths: function()
23018 var months = this.picker().select('>.datepicker-months td', true).first();
23020 months.dom.innerHTML = '';
23026 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23029 months.createChild(month);
23038 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23039 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23042 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23043 e.removeClass('active');
23045 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23046 e.addClass('active');
23053 if(this.isInline) {
23057 this.picker().removeClass(['bottom', 'top']);
23059 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23061 * place to the top of element!
23065 this.picker().addClass('top');
23066 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23071 this.picker().addClass('bottom');
23073 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23076 onFocus : function()
23078 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23082 onBlur : function()
23084 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23086 var d = this.inputEl().getValue();
23095 this.picker().show();
23096 this.picker().select('>.datepicker-months', true).first().show();
23100 this.fireEvent('show', this, this.date);
23105 if(this.isInline) {
23108 this.picker().hide();
23109 this.fireEvent('hide', this, this.date);
23113 onMousedown: function(e)
23115 e.stopPropagation();
23116 e.preventDefault();
23121 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23125 fireKey: function(e)
23127 if (!this.picker().isVisible()){
23128 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23139 e.preventDefault();
23143 dir = e.keyCode == 37 ? -1 : 1;
23145 this.vIndex = this.vIndex + dir;
23147 if(this.vIndex < 0){
23151 if(this.vIndex > 11){
23155 if(isNaN(this.vIndex)){
23159 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23165 dir = e.keyCode == 38 ? -1 : 1;
23167 this.vIndex = this.vIndex + dir * 4;
23169 if(this.vIndex < 0){
23173 if(this.vIndex > 11){
23177 if(isNaN(this.vIndex)){
23181 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23186 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23187 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23191 e.preventDefault();
23194 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23195 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23211 this.picker().remove();
23216 Roo.apply(Roo.bootstrap.MonthField, {
23235 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23236 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23241 Roo.apply(Roo.bootstrap.MonthField, {
23245 cls: 'datepicker dropdown-menu roo-dynamic',
23249 cls: 'datepicker-months',
23253 cls: 'table-condensed',
23255 Roo.bootstrap.DateField.content
23275 * @class Roo.bootstrap.CheckBox
23276 * @extends Roo.bootstrap.Input
23277 * Bootstrap CheckBox class
23279 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23280 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23281 * @cfg {String} boxLabel The text that appears beside the checkbox
23282 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23283 * @cfg {Boolean} checked initnal the element
23284 * @cfg {Boolean} inline inline the element (default false)
23285 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23286 * @cfg {String} tooltip label tooltip
23289 * Create a new CheckBox
23290 * @param {Object} config The config object
23293 Roo.bootstrap.CheckBox = function(config){
23294 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23299 * Fires when the element is checked or unchecked.
23300 * @param {Roo.bootstrap.CheckBox} this This input
23301 * @param {Boolean} checked The new checked value
23306 * Fires when the element is click.
23307 * @param {Roo.bootstrap.CheckBox} this This input
23314 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23316 inputType: 'checkbox',
23325 // checkbox success does not make any sense really..
23330 getAutoCreate : function()
23332 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23338 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23341 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23347 type : this.inputType,
23348 value : this.inputValue,
23349 cls : 'roo-' + this.inputType, //'form-box',
23350 placeholder : this.placeholder || ''
23354 if(this.inputType != 'radio'){
23358 cls : 'roo-hidden-value',
23359 value : this.checked ? this.inputValue : this.valueOff
23364 if (this.weight) { // Validity check?
23365 cfg.cls += " " + this.inputType + "-" + this.weight;
23368 if (this.disabled) {
23369 input.disabled=true;
23373 input.checked = this.checked;
23378 input.name = this.name;
23380 if(this.inputType != 'radio'){
23381 hidden.name = this.name;
23382 input.name = '_hidden_' + this.name;
23387 input.cls += ' input-' + this.size;
23392 ['xs','sm','md','lg'].map(function(size){
23393 if (settings[size]) {
23394 cfg.cls += ' col-' + size + '-' + settings[size];
23398 var inputblock = input;
23400 if (this.before || this.after) {
23403 cls : 'input-group',
23408 inputblock.cn.push({
23410 cls : 'input-group-addon',
23415 inputblock.cn.push(input);
23417 if(this.inputType != 'radio'){
23418 inputblock.cn.push(hidden);
23422 inputblock.cn.push({
23424 cls : 'input-group-addon',
23430 var boxLabelCfg = false;
23436 //'for': id, // box label is handled by onclick - so no for...
23438 html: this.boxLabel
23441 boxLabelCfg.tooltip = this.tooltip;
23447 if (align ==='left' && this.fieldLabel.length) {
23448 // Roo.log("left and has label");
23453 cls : 'control-label',
23454 html : this.fieldLabel
23465 cfg.cn[1].cn.push(boxLabelCfg);
23468 if(this.labelWidth > 12){
23469 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23472 if(this.labelWidth < 13 && this.labelmd == 0){
23473 this.labelmd = this.labelWidth;
23476 if(this.labellg > 0){
23477 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23478 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23481 if(this.labelmd > 0){
23482 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23483 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23486 if(this.labelsm > 0){
23487 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23488 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23491 if(this.labelxs > 0){
23492 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23493 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23496 } else if ( this.fieldLabel.length) {
23497 // Roo.log(" label");
23501 tag: this.boxLabel ? 'span' : 'label',
23503 cls: 'control-label box-input-label',
23504 //cls : 'input-group-addon',
23505 html : this.fieldLabel
23512 cfg.cn.push(boxLabelCfg);
23517 // Roo.log(" no label && no align");
23518 cfg.cn = [ inputblock ] ;
23520 cfg.cn.push(boxLabelCfg);
23528 if(this.inputType != 'radio'){
23529 cfg.cn.push(hidden);
23537 * return the real input element.
23539 inputEl: function ()
23541 return this.el.select('input.roo-' + this.inputType,true).first();
23543 hiddenEl: function ()
23545 return this.el.select('input.roo-hidden-value',true).first();
23548 labelEl: function()
23550 return this.el.select('label.control-label',true).first();
23552 /* depricated... */
23556 return this.labelEl();
23559 boxLabelEl: function()
23561 return this.el.select('label.box-label',true).first();
23564 initEvents : function()
23566 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23568 this.inputEl().on('click', this.onClick, this);
23570 if (this.boxLabel) {
23571 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23574 this.startValue = this.getValue();
23577 Roo.bootstrap.CheckBox.register(this);
23581 onClick : function(e)
23583 if(this.fireEvent('click', this, e) !== false){
23584 this.setChecked(!this.checked);
23589 setChecked : function(state,suppressEvent)
23591 this.startValue = this.getValue();
23593 if(this.inputType == 'radio'){
23595 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23596 e.dom.checked = false;
23599 this.inputEl().dom.checked = true;
23601 this.inputEl().dom.value = this.inputValue;
23603 if(suppressEvent !== true){
23604 this.fireEvent('check', this, true);
23612 this.checked = state;
23614 this.inputEl().dom.checked = state;
23617 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23619 if(suppressEvent !== true){
23620 this.fireEvent('check', this, state);
23626 getValue : function()
23628 if(this.inputType == 'radio'){
23629 return this.getGroupValue();
23632 return this.hiddenEl().dom.value;
23636 getGroupValue : function()
23638 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23642 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23645 setValue : function(v,suppressEvent)
23647 if(this.inputType == 'radio'){
23648 this.setGroupValue(v, suppressEvent);
23652 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23657 setGroupValue : function(v, suppressEvent)
23659 this.startValue = this.getValue();
23661 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23662 e.dom.checked = false;
23664 if(e.dom.value == v){
23665 e.dom.checked = true;
23669 if(suppressEvent !== true){
23670 this.fireEvent('check', this, true);
23678 validate : function()
23680 if(this.getVisibilityEl().hasClass('hidden')){
23686 (this.inputType == 'radio' && this.validateRadio()) ||
23687 (this.inputType == 'checkbox' && this.validateCheckbox())
23693 this.markInvalid();
23697 validateRadio : function()
23699 if(this.getVisibilityEl().hasClass('hidden')){
23703 if(this.allowBlank){
23709 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23710 if(!e.dom.checked){
23722 validateCheckbox : function()
23725 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23726 //return (this.getValue() == this.inputValue) ? true : false;
23729 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23737 for(var i in group){
23738 if(group[i].el.isVisible(true)){
23746 for(var i in group){
23751 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23758 * Mark this field as valid
23760 markValid : function()
23764 this.fireEvent('valid', this);
23766 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23769 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23776 if(this.inputType == 'radio'){
23777 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23778 var fg = e.findParent('.form-group', false, true);
23779 if (Roo.bootstrap.version == 3) {
23780 fg.removeClass([_this.invalidClass, _this.validClass]);
23781 fg.addClass(_this.validClass);
23783 fg.removeClass(['is-valid', 'is-invalid']);
23784 fg.addClass('is-valid');
23792 var fg = this.el.findParent('.form-group', false, true);
23793 if (Roo.bootstrap.version == 3) {
23794 fg.removeClass([this.invalidClass, this.validClass]);
23795 fg.addClass(this.validClass);
23797 fg.removeClass(['is-valid', 'is-invalid']);
23798 fg.addClass('is-valid');
23803 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23809 for(var i in group){
23810 var fg = group[i].el.findParent('.form-group', false, true);
23811 if (Roo.bootstrap.version == 3) {
23812 fg.removeClass([this.invalidClass, this.validClass]);
23813 fg.addClass(this.validClass);
23815 fg.removeClass(['is-valid', 'is-invalid']);
23816 fg.addClass('is-valid');
23822 * Mark this field as invalid
23823 * @param {String} msg The validation message
23825 markInvalid : function(msg)
23827 if(this.allowBlank){
23833 this.fireEvent('invalid', this, msg);
23835 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23838 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23842 label.markInvalid();
23845 if(this.inputType == 'radio'){
23847 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23848 var fg = e.findParent('.form-group', false, true);
23849 if (Roo.bootstrap.version == 3) {
23850 fg.removeClass([_this.invalidClass, _this.validClass]);
23851 fg.addClass(_this.invalidClass);
23853 fg.removeClass(['is-invalid', 'is-valid']);
23854 fg.addClass('is-invalid');
23862 var fg = this.el.findParent('.form-group', false, true);
23863 if (Roo.bootstrap.version == 3) {
23864 fg.removeClass([_this.invalidClass, _this.validClass]);
23865 fg.addClass(_this.invalidClass);
23867 fg.removeClass(['is-invalid', 'is-valid']);
23868 fg.addClass('is-invalid');
23873 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23879 for(var i in group){
23880 var fg = group[i].el.findParent('.form-group', false, true);
23881 if (Roo.bootstrap.version == 3) {
23882 fg.removeClass([_this.invalidClass, _this.validClass]);
23883 fg.addClass(_this.invalidClass);
23885 fg.removeClass(['is-invalid', 'is-valid']);
23886 fg.addClass('is-invalid');
23892 clearInvalid : function()
23894 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23896 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23898 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23900 if (label && label.iconEl) {
23901 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23902 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23906 disable : function()
23908 if(this.inputType != 'radio'){
23909 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23916 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23917 _this.getActionEl().addClass(this.disabledClass);
23918 e.dom.disabled = true;
23922 this.disabled = true;
23923 this.fireEvent("disable", this);
23927 enable : function()
23929 if(this.inputType != 'radio'){
23930 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23937 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23938 _this.getActionEl().removeClass(this.disabledClass);
23939 e.dom.disabled = false;
23943 this.disabled = false;
23944 this.fireEvent("enable", this);
23948 setBoxLabel : function(v)
23953 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23959 Roo.apply(Roo.bootstrap.CheckBox, {
23964 * register a CheckBox Group
23965 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23967 register : function(checkbox)
23969 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23970 this.groups[checkbox.groupId] = {};
23973 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23977 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23981 * fetch a CheckBox Group based on the group ID
23982 * @param {string} the group ID
23983 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23985 get: function(groupId) {
23986 if (typeof(this.groups[groupId]) == 'undefined') {
23990 return this.groups[groupId] ;
24003 * @class Roo.bootstrap.Radio
24004 * @extends Roo.bootstrap.Component
24005 * Bootstrap Radio class
24006 * @cfg {String} boxLabel - the label associated
24007 * @cfg {String} value - the value of radio
24010 * Create a new Radio
24011 * @param {Object} config The config object
24013 Roo.bootstrap.Radio = function(config){
24014 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24018 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24024 getAutoCreate : function()
24028 cls : 'form-group radio',
24033 html : this.boxLabel
24041 initEvents : function()
24043 this.parent().register(this);
24045 this.el.on('click', this.onClick, this);
24049 onClick : function(e)
24051 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24052 this.setChecked(true);
24056 setChecked : function(state, suppressEvent)
24058 this.parent().setValue(this.value, suppressEvent);
24062 setBoxLabel : function(v)
24067 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24082 * @class Roo.bootstrap.SecurePass
24083 * @extends Roo.bootstrap.Input
24084 * Bootstrap SecurePass class
24088 * Create a new SecurePass
24089 * @param {Object} config The config object
24092 Roo.bootstrap.SecurePass = function (config) {
24093 // these go here, so the translation tool can replace them..
24095 PwdEmpty: "Please type a password, and then retype it to confirm.",
24096 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24097 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24098 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24099 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24100 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24101 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24102 TooWeak: "Your password is Too Weak."
24104 this.meterLabel = "Password strength:";
24105 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24106 this.meterClass = [
24107 "roo-password-meter-tooweak",
24108 "roo-password-meter-weak",
24109 "roo-password-meter-medium",
24110 "roo-password-meter-strong",
24111 "roo-password-meter-grey"
24116 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24119 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24121 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24123 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24124 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24125 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24126 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24127 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24128 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24129 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24139 * @cfg {String/Object} Label for the strength meter (defaults to
24140 * 'Password strength:')
24145 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24146 * ['Weak', 'Medium', 'Strong'])
24149 pwdStrengths: false,
24162 initEvents: function ()
24164 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24166 if (this.el.is('input[type=password]') && Roo.isSafari) {
24167 this.el.on('keydown', this.SafariOnKeyDown, this);
24170 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24173 onRender: function (ct, position)
24175 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24176 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24177 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24179 this.trigger.createChild({
24184 cls: 'roo-password-meter-grey col-xs-12',
24187 //width: this.meterWidth + 'px'
24191 cls: 'roo-password-meter-text'
24197 if (this.hideTrigger) {
24198 this.trigger.setDisplayed(false);
24200 this.setSize(this.width || '', this.height || '');
24203 onDestroy: function ()
24205 if (this.trigger) {
24206 this.trigger.removeAllListeners();
24207 this.trigger.remove();
24210 this.wrap.remove();
24212 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24215 checkStrength: function ()
24217 var pwd = this.inputEl().getValue();
24218 if (pwd == this._lastPwd) {
24223 if (this.ClientSideStrongPassword(pwd)) {
24225 } else if (this.ClientSideMediumPassword(pwd)) {
24227 } else if (this.ClientSideWeakPassword(pwd)) {
24233 Roo.log('strength1: ' + strength);
24235 //var pm = this.trigger.child('div/div/div').dom;
24236 var pm = this.trigger.child('div/div');
24237 pm.removeClass(this.meterClass);
24238 pm.addClass(this.meterClass[strength]);
24241 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24243 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24245 this._lastPwd = pwd;
24249 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24251 this._lastPwd = '';
24253 var pm = this.trigger.child('div/div');
24254 pm.removeClass(this.meterClass);
24255 pm.addClass('roo-password-meter-grey');
24258 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24261 this.inputEl().dom.type='password';
24264 validateValue: function (value)
24266 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24269 if (value.length == 0) {
24270 if (this.allowBlank) {
24271 this.clearInvalid();
24275 this.markInvalid(this.errors.PwdEmpty);
24276 this.errorMsg = this.errors.PwdEmpty;
24284 if (!value.match(/[\x21-\x7e]+/)) {
24285 this.markInvalid(this.errors.PwdBadChar);
24286 this.errorMsg = this.errors.PwdBadChar;
24289 if (value.length < 6) {
24290 this.markInvalid(this.errors.PwdShort);
24291 this.errorMsg = this.errors.PwdShort;
24294 if (value.length > 16) {
24295 this.markInvalid(this.errors.PwdLong);
24296 this.errorMsg = this.errors.PwdLong;
24300 if (this.ClientSideStrongPassword(value)) {
24302 } else if (this.ClientSideMediumPassword(value)) {
24304 } else if (this.ClientSideWeakPassword(value)) {
24311 if (strength < 2) {
24312 //this.markInvalid(this.errors.TooWeak);
24313 this.errorMsg = this.errors.TooWeak;
24318 console.log('strength2: ' + strength);
24320 //var pm = this.trigger.child('div/div/div').dom;
24322 var pm = this.trigger.child('div/div');
24323 pm.removeClass(this.meterClass);
24324 pm.addClass(this.meterClass[strength]);
24326 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24328 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24330 this.errorMsg = '';
24334 CharacterSetChecks: function (type)
24337 this.fResult = false;
24340 isctype: function (character, type)
24343 case this.kCapitalLetter:
24344 if (character >= 'A' && character <= 'Z') {
24349 case this.kSmallLetter:
24350 if (character >= 'a' && character <= 'z') {
24356 if (character >= '0' && character <= '9') {
24361 case this.kPunctuation:
24362 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24373 IsLongEnough: function (pwd, size)
24375 return !(pwd == null || isNaN(size) || pwd.length < size);
24378 SpansEnoughCharacterSets: function (word, nb)
24380 if (!this.IsLongEnough(word, nb))
24385 var characterSetChecks = new Array(
24386 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24387 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24390 for (var index = 0; index < word.length; ++index) {
24391 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24392 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24393 characterSetChecks[nCharSet].fResult = true;
24400 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24401 if (characterSetChecks[nCharSet].fResult) {
24406 if (nCharSets < nb) {
24412 ClientSideStrongPassword: function (pwd)
24414 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24417 ClientSideMediumPassword: function (pwd)
24419 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24422 ClientSideWeakPassword: function (pwd)
24424 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24427 })//<script type="text/javascript">
24430 * Based Ext JS Library 1.1.1
24431 * Copyright(c) 2006-2007, Ext JS, LLC.
24437 * @class Roo.HtmlEditorCore
24438 * @extends Roo.Component
24439 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24441 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24444 Roo.HtmlEditorCore = function(config){
24447 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24452 * @event initialize
24453 * Fires when the editor is fully initialized (including the iframe)
24454 * @param {Roo.HtmlEditorCore} this
24459 * Fires when the editor is first receives the focus. Any insertion must wait
24460 * until after this event.
24461 * @param {Roo.HtmlEditorCore} this
24465 * @event beforesync
24466 * Fires before the textarea is updated with content from the editor iframe. Return false
24467 * to cancel the sync.
24468 * @param {Roo.HtmlEditorCore} this
24469 * @param {String} html
24473 * @event beforepush
24474 * Fires before the iframe editor is updated with content from the textarea. Return false
24475 * to cancel the push.
24476 * @param {Roo.HtmlEditorCore} this
24477 * @param {String} html
24482 * Fires when the textarea is updated with content from the editor iframe.
24483 * @param {Roo.HtmlEditorCore} this
24484 * @param {String} html
24489 * Fires when the iframe editor is updated with content from the textarea.
24490 * @param {Roo.HtmlEditorCore} this
24491 * @param {String} html
24496 * @event editorevent
24497 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24498 * @param {Roo.HtmlEditorCore} this
24504 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24506 // defaults : white / black...
24507 this.applyBlacklists();
24514 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24518 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24524 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24529 * @cfg {Number} height (in pixels)
24533 * @cfg {Number} width (in pixels)
24538 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24541 stylesheets: false,
24546 // private properties
24547 validationEvent : false,
24549 initialized : false,
24551 sourceEditMode : false,
24552 onFocus : Roo.emptyFn,
24554 hideMode:'offsets',
24558 // blacklist + whitelisted elements..
24565 * Protected method that will not generally be called directly. It
24566 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24567 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24569 getDocMarkup : function(){
24573 // inherit styels from page...??
24574 if (this.stylesheets === false) {
24576 Roo.get(document.head).select('style').each(function(node) {
24577 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24580 Roo.get(document.head).select('link').each(function(node) {
24581 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24584 } else if (!this.stylesheets.length) {
24586 st = '<style type="text/css">' +
24587 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24590 for (var i in this.stylesheets) {
24591 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24596 st += '<style type="text/css">' +
24597 'IMG { cursor: pointer } ' +
24600 var cls = 'roo-htmleditor-body';
24602 if(this.bodyCls.length){
24603 cls += ' ' + this.bodyCls;
24606 return '<html><head>' + st +
24607 //<style type="text/css">' +
24608 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24610 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24614 onRender : function(ct, position)
24617 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24618 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24621 this.el.dom.style.border = '0 none';
24622 this.el.dom.setAttribute('tabIndex', -1);
24623 this.el.addClass('x-hidden hide');
24627 if(Roo.isIE){ // fix IE 1px bogus margin
24628 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24632 this.frameId = Roo.id();
24636 var iframe = this.owner.wrap.createChild({
24638 cls: 'form-control', // bootstrap..
24640 name: this.frameId,
24641 frameBorder : 'no',
24642 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24647 this.iframe = iframe.dom;
24649 this.assignDocWin();
24651 this.doc.designMode = 'on';
24654 this.doc.write(this.getDocMarkup());
24658 var task = { // must defer to wait for browser to be ready
24660 //console.log("run task?" + this.doc.readyState);
24661 this.assignDocWin();
24662 if(this.doc.body || this.doc.readyState == 'complete'){
24664 this.doc.designMode="on";
24668 Roo.TaskMgr.stop(task);
24669 this.initEditor.defer(10, this);
24676 Roo.TaskMgr.start(task);
24681 onResize : function(w, h)
24683 Roo.log('resize: ' +w + ',' + h );
24684 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24688 if(typeof w == 'number'){
24690 this.iframe.style.width = w + 'px';
24692 if(typeof h == 'number'){
24694 this.iframe.style.height = h + 'px';
24696 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24703 * Toggles the editor between standard and source edit mode.
24704 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24706 toggleSourceEdit : function(sourceEditMode){
24708 this.sourceEditMode = sourceEditMode === true;
24710 if(this.sourceEditMode){
24712 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24715 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24716 //this.iframe.className = '';
24719 //this.setSize(this.owner.wrap.getSize());
24720 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24727 * Protected method that will not generally be called directly. If you need/want
24728 * custom HTML cleanup, this is the method you should override.
24729 * @param {String} html The HTML to be cleaned
24730 * return {String} The cleaned HTML
24732 cleanHtml : function(html){
24733 html = String(html);
24734 if(html.length > 5){
24735 if(Roo.isSafari){ // strip safari nonsense
24736 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24739 if(html == ' '){
24746 * HTML Editor -> Textarea
24747 * Protected method that will not generally be called directly. Syncs the contents
24748 * of the editor iframe with the textarea.
24750 syncValue : function(){
24751 if(this.initialized){
24752 var bd = (this.doc.body || this.doc.documentElement);
24753 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24754 var html = bd.innerHTML;
24756 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24757 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24759 html = '<div style="'+m[0]+'">' + html + '</div>';
24762 html = this.cleanHtml(html);
24763 // fix up the special chars.. normaly like back quotes in word...
24764 // however we do not want to do this with chinese..
24765 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24767 var cc = match.charCodeAt();
24769 // Get the character value, handling surrogate pairs
24770 if (match.length == 2) {
24771 // It's a surrogate pair, calculate the Unicode code point
24772 var high = match.charCodeAt(0) - 0xD800;
24773 var low = match.charCodeAt(1) - 0xDC00;
24774 cc = (high * 0x400) + low + 0x10000;
24776 (cc >= 0x4E00 && cc < 0xA000 ) ||
24777 (cc >= 0x3400 && cc < 0x4E00 ) ||
24778 (cc >= 0xf900 && cc < 0xfb00 )
24783 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24784 return "&#" + cc + ";";
24791 if(this.owner.fireEvent('beforesync', this, html) !== false){
24792 this.el.dom.value = html;
24793 this.owner.fireEvent('sync', this, html);
24799 * Protected method that will not generally be called directly. Pushes the value of the textarea
24800 * into the iframe editor.
24802 pushValue : function(){
24803 if(this.initialized){
24804 var v = this.el.dom.value.trim();
24806 // if(v.length < 1){
24810 if(this.owner.fireEvent('beforepush', this, v) !== false){
24811 var d = (this.doc.body || this.doc.documentElement);
24813 this.cleanUpPaste();
24814 this.el.dom.value = d.innerHTML;
24815 this.owner.fireEvent('push', this, v);
24821 deferFocus : function(){
24822 this.focus.defer(10, this);
24826 focus : function(){
24827 if(this.win && !this.sourceEditMode){
24834 assignDocWin: function()
24836 var iframe = this.iframe;
24839 this.doc = iframe.contentWindow.document;
24840 this.win = iframe.contentWindow;
24842 // if (!Roo.get(this.frameId)) {
24845 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24846 // this.win = Roo.get(this.frameId).dom.contentWindow;
24848 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24852 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24853 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24858 initEditor : function(){
24859 //console.log("INIT EDITOR");
24860 this.assignDocWin();
24864 this.doc.designMode="on";
24866 this.doc.write(this.getDocMarkup());
24869 var dbody = (this.doc.body || this.doc.documentElement);
24870 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24871 // this copies styles from the containing element into thsi one..
24872 // not sure why we need all of this..
24873 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24875 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24876 //ss['background-attachment'] = 'fixed'; // w3c
24877 dbody.bgProperties = 'fixed'; // ie
24878 //Roo.DomHelper.applyStyles(dbody, ss);
24879 Roo.EventManager.on(this.doc, {
24880 //'mousedown': this.onEditorEvent,
24881 'mouseup': this.onEditorEvent,
24882 'dblclick': this.onEditorEvent,
24883 'click': this.onEditorEvent,
24884 'keyup': this.onEditorEvent,
24889 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24891 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24892 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24894 this.initialized = true;
24896 this.owner.fireEvent('initialize', this);
24901 onDestroy : function(){
24907 //for (var i =0; i < this.toolbars.length;i++) {
24908 // // fixme - ask toolbars for heights?
24909 // this.toolbars[i].onDestroy();
24912 //this.wrap.dom.innerHTML = '';
24913 //this.wrap.remove();
24918 onFirstFocus : function(){
24920 this.assignDocWin();
24923 this.activated = true;
24926 if(Roo.isGecko){ // prevent silly gecko errors
24928 var s = this.win.getSelection();
24929 if(!s.focusNode || s.focusNode.nodeType != 3){
24930 var r = s.getRangeAt(0);
24931 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24936 this.execCmd('useCSS', true);
24937 this.execCmd('styleWithCSS', false);
24940 this.owner.fireEvent('activate', this);
24944 adjustFont: function(btn){
24945 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24946 //if(Roo.isSafari){ // safari
24949 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24950 if(Roo.isSafari){ // safari
24951 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24952 v = (v < 10) ? 10 : v;
24953 v = (v > 48) ? 48 : v;
24954 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24959 v = Math.max(1, v+adjust);
24961 this.execCmd('FontSize', v );
24964 onEditorEvent : function(e)
24966 this.owner.fireEvent('editorevent', this, e);
24967 // this.updateToolbar();
24968 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24971 insertTag : function(tg)
24973 // could be a bit smarter... -> wrap the current selected tRoo..
24974 if (tg.toLowerCase() == 'span' ||
24975 tg.toLowerCase() == 'code' ||
24976 tg.toLowerCase() == 'sup' ||
24977 tg.toLowerCase() == 'sub'
24980 range = this.createRange(this.getSelection());
24981 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24982 wrappingNode.appendChild(range.extractContents());
24983 range.insertNode(wrappingNode);
24990 this.execCmd("formatblock", tg);
24994 insertText : function(txt)
24998 var range = this.createRange();
24999 range.deleteContents();
25000 //alert(Sender.getAttribute('label'));
25002 range.insertNode(this.doc.createTextNode(txt));
25008 * Executes a Midas editor command on the editor document and performs necessary focus and
25009 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25010 * @param {String} cmd The Midas command
25011 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25013 relayCmd : function(cmd, value){
25015 this.execCmd(cmd, value);
25016 this.owner.fireEvent('editorevent', this);
25017 //this.updateToolbar();
25018 this.owner.deferFocus();
25022 * Executes a Midas editor command directly on the editor document.
25023 * For visual commands, you should use {@link #relayCmd} instead.
25024 * <b>This should only be called after the editor is initialized.</b>
25025 * @param {String} cmd The Midas command
25026 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25028 execCmd : function(cmd, value){
25029 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25036 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25038 * @param {String} text | dom node..
25040 insertAtCursor : function(text)
25043 if(!this.activated){
25049 var r = this.doc.selection.createRange();
25060 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25064 // from jquery ui (MIT licenced)
25066 var win = this.win;
25068 if (win.getSelection && win.getSelection().getRangeAt) {
25069 range = win.getSelection().getRangeAt(0);
25070 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25071 range.insertNode(node);
25072 } else if (win.document.selection && win.document.selection.createRange) {
25073 // no firefox support
25074 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25075 win.document.selection.createRange().pasteHTML(txt);
25077 // no firefox support
25078 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25079 this.execCmd('InsertHTML', txt);
25088 mozKeyPress : function(e){
25090 var c = e.getCharCode(), cmd;
25093 c = String.fromCharCode(c).toLowerCase();
25107 this.cleanUpPaste.defer(100, this);
25115 e.preventDefault();
25123 fixKeys : function(){ // load time branching for fastest keydown performance
25125 return function(e){
25126 var k = e.getKey(), r;
25129 r = this.doc.selection.createRange();
25132 r.pasteHTML('    ');
25139 r = this.doc.selection.createRange();
25141 var target = r.parentElement();
25142 if(!target || target.tagName.toLowerCase() != 'li'){
25144 r.pasteHTML('<br />');
25150 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25151 this.cleanUpPaste.defer(100, this);
25157 }else if(Roo.isOpera){
25158 return function(e){
25159 var k = e.getKey();
25163 this.execCmd('InsertHTML','    ');
25166 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25167 this.cleanUpPaste.defer(100, this);
25172 }else if(Roo.isSafari){
25173 return function(e){
25174 var k = e.getKey();
25178 this.execCmd('InsertText','\t');
25182 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25183 this.cleanUpPaste.defer(100, this);
25191 getAllAncestors: function()
25193 var p = this.getSelectedNode();
25196 a.push(p); // push blank onto stack..
25197 p = this.getParentElement();
25201 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25205 a.push(this.doc.body);
25209 lastSelNode : false,
25212 getSelection : function()
25214 this.assignDocWin();
25215 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25218 getSelectedNode: function()
25220 // this may only work on Gecko!!!
25222 // should we cache this!!!!
25227 var range = this.createRange(this.getSelection()).cloneRange();
25230 var parent = range.parentElement();
25232 var testRange = range.duplicate();
25233 testRange.moveToElementText(parent);
25234 if (testRange.inRange(range)) {
25237 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25240 parent = parent.parentElement;
25245 // is ancestor a text element.
25246 var ac = range.commonAncestorContainer;
25247 if (ac.nodeType == 3) {
25248 ac = ac.parentNode;
25251 var ar = ac.childNodes;
25254 var other_nodes = [];
25255 var has_other_nodes = false;
25256 for (var i=0;i<ar.length;i++) {
25257 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25260 // fullly contained node.
25262 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25267 // probably selected..
25268 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25269 other_nodes.push(ar[i]);
25273 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25278 has_other_nodes = true;
25280 if (!nodes.length && other_nodes.length) {
25281 nodes= other_nodes;
25283 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25289 createRange: function(sel)
25291 // this has strange effects when using with
25292 // top toolbar - not sure if it's a great idea.
25293 //this.editor.contentWindow.focus();
25294 if (typeof sel != "undefined") {
25296 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25298 return this.doc.createRange();
25301 return this.doc.createRange();
25304 getParentElement: function()
25307 this.assignDocWin();
25308 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25310 var range = this.createRange(sel);
25313 var p = range.commonAncestorContainer;
25314 while (p.nodeType == 3) { // text node
25325 * Range intersection.. the hard stuff...
25329 * [ -- selected range --- ]
25333 * if end is before start or hits it. fail.
25334 * if start is after end or hits it fail.
25336 * if either hits (but other is outside. - then it's not
25342 // @see http://www.thismuchiknow.co.uk/?p=64.
25343 rangeIntersectsNode : function(range, node)
25345 var nodeRange = node.ownerDocument.createRange();
25347 nodeRange.selectNode(node);
25349 nodeRange.selectNodeContents(node);
25352 var rangeStartRange = range.cloneRange();
25353 rangeStartRange.collapse(true);
25355 var rangeEndRange = range.cloneRange();
25356 rangeEndRange.collapse(false);
25358 var nodeStartRange = nodeRange.cloneRange();
25359 nodeStartRange.collapse(true);
25361 var nodeEndRange = nodeRange.cloneRange();
25362 nodeEndRange.collapse(false);
25364 return rangeStartRange.compareBoundaryPoints(
25365 Range.START_TO_START, nodeEndRange) == -1 &&
25366 rangeEndRange.compareBoundaryPoints(
25367 Range.START_TO_START, nodeStartRange) == 1;
25371 rangeCompareNode : function(range, node)
25373 var nodeRange = node.ownerDocument.createRange();
25375 nodeRange.selectNode(node);
25377 nodeRange.selectNodeContents(node);
25381 range.collapse(true);
25383 nodeRange.collapse(true);
25385 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25386 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25388 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25390 var nodeIsBefore = ss == 1;
25391 var nodeIsAfter = ee == -1;
25393 if (nodeIsBefore && nodeIsAfter) {
25396 if (!nodeIsBefore && nodeIsAfter) {
25397 return 1; //right trailed.
25400 if (nodeIsBefore && !nodeIsAfter) {
25401 return 2; // left trailed.
25407 // private? - in a new class?
25408 cleanUpPaste : function()
25410 // cleans up the whole document..
25411 Roo.log('cleanuppaste');
25413 this.cleanUpChildren(this.doc.body);
25414 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25415 if (clean != this.doc.body.innerHTML) {
25416 this.doc.body.innerHTML = clean;
25421 cleanWordChars : function(input) {// change the chars to hex code
25422 var he = Roo.HtmlEditorCore;
25424 var output = input;
25425 Roo.each(he.swapCodes, function(sw) {
25426 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25428 output = output.replace(swapper, sw[1]);
25435 cleanUpChildren : function (n)
25437 if (!n.childNodes.length) {
25440 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25441 this.cleanUpChild(n.childNodes[i]);
25448 cleanUpChild : function (node)
25451 //console.log(node);
25452 if (node.nodeName == "#text") {
25453 // clean up silly Windows -- stuff?
25456 if (node.nodeName == "#comment") {
25457 node.parentNode.removeChild(node);
25458 // clean up silly Windows -- stuff?
25461 var lcname = node.tagName.toLowerCase();
25462 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25463 // whitelist of tags..
25465 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25467 node.parentNode.removeChild(node);
25472 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25474 // spans with no attributes - just remove them..
25475 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25476 remove_keep_children = true;
25479 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25480 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25482 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25483 // remove_keep_children = true;
25486 if (remove_keep_children) {
25487 this.cleanUpChildren(node);
25488 // inserts everything just before this node...
25489 while (node.childNodes.length) {
25490 var cn = node.childNodes[0];
25491 node.removeChild(cn);
25492 node.parentNode.insertBefore(cn, node);
25494 node.parentNode.removeChild(node);
25498 if (!node.attributes || !node.attributes.length) {
25503 this.cleanUpChildren(node);
25507 function cleanAttr(n,v)
25510 if (v.match(/^\./) || v.match(/^\//)) {
25513 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25516 if (v.match(/^#/)) {
25519 if (v.match(/^\{/)) { // allow template editing.
25522 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25523 node.removeAttribute(n);
25527 var cwhite = this.cwhite;
25528 var cblack = this.cblack;
25530 function cleanStyle(n,v)
25532 if (v.match(/expression/)) { //XSS?? should we even bother..
25533 node.removeAttribute(n);
25537 var parts = v.split(/;/);
25540 Roo.each(parts, function(p) {
25541 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25545 var l = p.split(':').shift().replace(/\s+/g,'');
25546 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25548 if ( cwhite.length && cblack.indexOf(l) > -1) {
25549 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25550 //node.removeAttribute(n);
25554 // only allow 'c whitelisted system attributes'
25555 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25556 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25557 //node.removeAttribute(n);
25567 if (clean.length) {
25568 node.setAttribute(n, clean.join(';'));
25570 node.removeAttribute(n);
25576 for (var i = node.attributes.length-1; i > -1 ; i--) {
25577 var a = node.attributes[i];
25580 if (a.name.toLowerCase().substr(0,2)=='on') {
25581 node.removeAttribute(a.name);
25584 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25585 node.removeAttribute(a.name);
25588 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25589 cleanAttr(a.name,a.value); // fixme..
25592 if (a.name == 'style') {
25593 cleanStyle(a.name,a.value);
25596 /// clean up MS crap..
25597 // tecnically this should be a list of valid class'es..
25600 if (a.name == 'class') {
25601 if (a.value.match(/^Mso/)) {
25602 node.removeAttribute('class');
25605 if (a.value.match(/^body$/)) {
25606 node.removeAttribute('class');
25617 this.cleanUpChildren(node);
25623 * Clean up MS wordisms...
25625 cleanWord : function(node)
25628 this.cleanWord(this.doc.body);
25633 node.nodeName == 'SPAN' &&
25634 !node.hasAttributes() &&
25635 node.childNodes.length == 1 &&
25636 node.firstChild.nodeName == "#text"
25638 var textNode = node.firstChild;
25639 node.removeChild(textNode);
25640 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25641 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25643 node.parentNode.insertBefore(textNode, node);
25644 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25645 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25647 node.parentNode.removeChild(node);
25650 if (node.nodeName == "#text") {
25651 // clean up silly Windows -- stuff?
25654 if (node.nodeName == "#comment") {
25655 node.parentNode.removeChild(node);
25656 // clean up silly Windows -- stuff?
25660 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25661 node.parentNode.removeChild(node);
25664 //Roo.log(node.tagName);
25665 // remove - but keep children..
25666 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25667 //Roo.log('-- removed');
25668 while (node.childNodes.length) {
25669 var cn = node.childNodes[0];
25670 node.removeChild(cn);
25671 node.parentNode.insertBefore(cn, node);
25672 // move node to parent - and clean it..
25673 this.cleanWord(cn);
25675 node.parentNode.removeChild(node);
25676 /// no need to iterate chidlren = it's got none..
25677 //this.iterateChildren(node, this.cleanWord);
25681 if (node.className.length) {
25683 var cn = node.className.split(/\W+/);
25685 Roo.each(cn, function(cls) {
25686 if (cls.match(/Mso[a-zA-Z]+/)) {
25691 node.className = cna.length ? cna.join(' ') : '';
25693 node.removeAttribute("class");
25697 if (node.hasAttribute("lang")) {
25698 node.removeAttribute("lang");
25701 if (node.hasAttribute("style")) {
25703 var styles = node.getAttribute("style").split(";");
25705 Roo.each(styles, function(s) {
25706 if (!s.match(/:/)) {
25709 var kv = s.split(":");
25710 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25713 // what ever is left... we allow.
25716 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25717 if (!nstyle.length) {
25718 node.removeAttribute('style');
25721 this.iterateChildren(node, this.cleanWord);
25727 * iterateChildren of a Node, calling fn each time, using this as the scole..
25728 * @param {DomNode} node node to iterate children of.
25729 * @param {Function} fn method of this class to call on each item.
25731 iterateChildren : function(node, fn)
25733 if (!node.childNodes.length) {
25736 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25737 fn.call(this, node.childNodes[i])
25743 * cleanTableWidths.
25745 * Quite often pasting from word etc.. results in tables with column and widths.
25746 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25749 cleanTableWidths : function(node)
25754 this.cleanTableWidths(this.doc.body);
25759 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25762 Roo.log(node.tagName);
25763 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25764 this.iterateChildren(node, this.cleanTableWidths);
25767 if (node.hasAttribute('width')) {
25768 node.removeAttribute('width');
25772 if (node.hasAttribute("style")) {
25775 var styles = node.getAttribute("style").split(";");
25777 Roo.each(styles, function(s) {
25778 if (!s.match(/:/)) {
25781 var kv = s.split(":");
25782 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25785 // what ever is left... we allow.
25788 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25789 if (!nstyle.length) {
25790 node.removeAttribute('style');
25794 this.iterateChildren(node, this.cleanTableWidths);
25802 domToHTML : function(currentElement, depth, nopadtext) {
25804 depth = depth || 0;
25805 nopadtext = nopadtext || false;
25807 if (!currentElement) {
25808 return this.domToHTML(this.doc.body);
25811 //Roo.log(currentElement);
25813 var allText = false;
25814 var nodeName = currentElement.nodeName;
25815 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25817 if (nodeName == '#text') {
25819 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25824 if (nodeName != 'BODY') {
25827 // Prints the node tagName, such as <A>, <IMG>, etc
25830 for(i = 0; i < currentElement.attributes.length;i++) {
25832 var aname = currentElement.attributes.item(i).name;
25833 if (!currentElement.attributes.item(i).value.length) {
25836 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25839 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25848 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25851 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25856 // Traverse the tree
25858 var currentElementChild = currentElement.childNodes.item(i);
25859 var allText = true;
25860 var innerHTML = '';
25862 while (currentElementChild) {
25863 // Formatting code (indent the tree so it looks nice on the screen)
25864 var nopad = nopadtext;
25865 if (lastnode == 'SPAN') {
25869 if (currentElementChild.nodeName == '#text') {
25870 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25871 toadd = nopadtext ? toadd : toadd.trim();
25872 if (!nopad && toadd.length > 80) {
25873 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25875 innerHTML += toadd;
25878 currentElementChild = currentElement.childNodes.item(i);
25884 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25886 // Recursively traverse the tree structure of the child node
25887 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25888 lastnode = currentElementChild.nodeName;
25890 currentElementChild=currentElement.childNodes.item(i);
25896 // The remaining code is mostly for formatting the tree
25897 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25902 ret+= "</"+tagName+">";
25908 applyBlacklists : function()
25910 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25911 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25915 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25916 if (b.indexOf(tag) > -1) {
25919 this.white.push(tag);
25923 Roo.each(w, function(tag) {
25924 if (b.indexOf(tag) > -1) {
25927 if (this.white.indexOf(tag) > -1) {
25930 this.white.push(tag);
25935 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25936 if (w.indexOf(tag) > -1) {
25939 this.black.push(tag);
25943 Roo.each(b, function(tag) {
25944 if (w.indexOf(tag) > -1) {
25947 if (this.black.indexOf(tag) > -1) {
25950 this.black.push(tag);
25955 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25956 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25960 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25961 if (b.indexOf(tag) > -1) {
25964 this.cwhite.push(tag);
25968 Roo.each(w, function(tag) {
25969 if (b.indexOf(tag) > -1) {
25972 if (this.cwhite.indexOf(tag) > -1) {
25975 this.cwhite.push(tag);
25980 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25981 if (w.indexOf(tag) > -1) {
25984 this.cblack.push(tag);
25988 Roo.each(b, function(tag) {
25989 if (w.indexOf(tag) > -1) {
25992 if (this.cblack.indexOf(tag) > -1) {
25995 this.cblack.push(tag);
26000 setStylesheets : function(stylesheets)
26002 if(typeof(stylesheets) == 'string'){
26003 Roo.get(this.iframe.contentDocument.head).createChild({
26005 rel : 'stylesheet',
26014 Roo.each(stylesheets, function(s) {
26019 Roo.get(_this.iframe.contentDocument.head).createChild({
26021 rel : 'stylesheet',
26030 removeStylesheets : function()
26034 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26039 setStyle : function(style)
26041 Roo.get(this.iframe.contentDocument.head).createChild({
26050 // hide stuff that is not compatible
26064 * @event specialkey
26068 * @cfg {String} fieldClass @hide
26071 * @cfg {String} focusClass @hide
26074 * @cfg {String} autoCreate @hide
26077 * @cfg {String} inputType @hide
26080 * @cfg {String} invalidClass @hide
26083 * @cfg {String} invalidText @hide
26086 * @cfg {String} msgFx @hide
26089 * @cfg {String} validateOnBlur @hide
26093 Roo.HtmlEditorCore.white = [
26094 'area', 'br', 'img', 'input', 'hr', 'wbr',
26096 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26097 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26098 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26099 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26100 'table', 'ul', 'xmp',
26102 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26105 'dir', 'menu', 'ol', 'ul', 'dl',
26111 Roo.HtmlEditorCore.black = [
26112 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26114 'base', 'basefont', 'bgsound', 'blink', 'body',
26115 'frame', 'frameset', 'head', 'html', 'ilayer',
26116 'iframe', 'layer', 'link', 'meta', 'object',
26117 'script', 'style' ,'title', 'xml' // clean later..
26119 Roo.HtmlEditorCore.clean = [
26120 'script', 'style', 'title', 'xml'
26122 Roo.HtmlEditorCore.remove = [
26127 Roo.HtmlEditorCore.ablack = [
26131 Roo.HtmlEditorCore.aclean = [
26132 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26136 Roo.HtmlEditorCore.pwhite= [
26137 'http', 'https', 'mailto'
26140 // white listed style attributes.
26141 Roo.HtmlEditorCore.cwhite= [
26142 // 'text-align', /// default is to allow most things..
26148 // black listed style attributes.
26149 Roo.HtmlEditorCore.cblack= [
26150 // 'font-size' -- this can be set by the project
26154 Roo.HtmlEditorCore.swapCodes =[
26155 [ 8211, "–" ],
26156 [ 8212, "—" ],
26173 * @class Roo.bootstrap.HtmlEditor
26174 * @extends Roo.bootstrap.TextArea
26175 * Bootstrap HtmlEditor class
26178 * Create a new HtmlEditor
26179 * @param {Object} config The config object
26182 Roo.bootstrap.HtmlEditor = function(config){
26183 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26184 if (!this.toolbars) {
26185 this.toolbars = [];
26188 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26191 * @event initialize
26192 * Fires when the editor is fully initialized (including the iframe)
26193 * @param {HtmlEditor} this
26198 * Fires when the editor is first receives the focus. Any insertion must wait
26199 * until after this event.
26200 * @param {HtmlEditor} this
26204 * @event beforesync
26205 * Fires before the textarea is updated with content from the editor iframe. Return false
26206 * to cancel the sync.
26207 * @param {HtmlEditor} this
26208 * @param {String} html
26212 * @event beforepush
26213 * Fires before the iframe editor is updated with content from the textarea. Return false
26214 * to cancel the push.
26215 * @param {HtmlEditor} this
26216 * @param {String} html
26221 * Fires when the textarea is updated with content from the editor iframe.
26222 * @param {HtmlEditor} this
26223 * @param {String} html
26228 * Fires when the iframe editor is updated with content from the textarea.
26229 * @param {HtmlEditor} this
26230 * @param {String} html
26234 * @event editmodechange
26235 * Fires when the editor switches edit modes
26236 * @param {HtmlEditor} this
26237 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26239 editmodechange: true,
26241 * @event editorevent
26242 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26243 * @param {HtmlEditor} this
26247 * @event firstfocus
26248 * Fires when on first focus - needed by toolbars..
26249 * @param {HtmlEditor} this
26254 * Auto save the htmlEditor value as a file into Events
26255 * @param {HtmlEditor} this
26259 * @event savedpreview
26260 * preview the saved version of htmlEditor
26261 * @param {HtmlEditor} this
26268 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26272 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26277 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26282 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26287 * @cfg {Number} height (in pixels)
26291 * @cfg {Number} width (in pixels)
26296 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26299 stylesheets: false,
26304 // private properties
26305 validationEvent : false,
26307 initialized : false,
26310 onFocus : Roo.emptyFn,
26312 hideMode:'offsets',
26314 tbContainer : false,
26318 toolbarContainer :function() {
26319 return this.wrap.select('.x-html-editor-tb',true).first();
26323 * Protected method that will not generally be called directly. It
26324 * is called when the editor creates its toolbar. Override this method if you need to
26325 * add custom toolbar buttons.
26326 * @param {HtmlEditor} editor
26328 createToolbar : function(){
26329 Roo.log('renewing');
26330 Roo.log("create toolbars");
26332 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26333 this.toolbars[0].render(this.toolbarContainer());
26337 // if (!editor.toolbars || !editor.toolbars.length) {
26338 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26341 // for (var i =0 ; i < editor.toolbars.length;i++) {
26342 // editor.toolbars[i] = Roo.factory(
26343 // typeof(editor.toolbars[i]) == 'string' ?
26344 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26345 // Roo.bootstrap.HtmlEditor);
26346 // editor.toolbars[i].init(editor);
26352 onRender : function(ct, position)
26354 // Roo.log("Call onRender: " + this.xtype);
26356 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26358 this.wrap = this.inputEl().wrap({
26359 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26362 this.editorcore.onRender(ct, position);
26364 if (this.resizable) {
26365 this.resizeEl = new Roo.Resizable(this.wrap, {
26369 minHeight : this.height,
26370 height: this.height,
26371 handles : this.resizable,
26374 resize : function(r, w, h) {
26375 _t.onResize(w,h); // -something
26381 this.createToolbar(this);
26384 if(!this.width && this.resizable){
26385 this.setSize(this.wrap.getSize());
26387 if (this.resizeEl) {
26388 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26389 // should trigger onReize..
26395 onResize : function(w, h)
26397 Roo.log('resize: ' +w + ',' + h );
26398 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26402 if(this.inputEl() ){
26403 if(typeof w == 'number'){
26404 var aw = w - this.wrap.getFrameWidth('lr');
26405 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26408 if(typeof h == 'number'){
26409 var tbh = -11; // fixme it needs to tool bar size!
26410 for (var i =0; i < this.toolbars.length;i++) {
26411 // fixme - ask toolbars for heights?
26412 tbh += this.toolbars[i].el.getHeight();
26413 //if (this.toolbars[i].footer) {
26414 // tbh += this.toolbars[i].footer.el.getHeight();
26422 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26423 ah -= 5; // knock a few pixes off for look..
26424 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26428 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26429 this.editorcore.onResize(ew,eh);
26434 * Toggles the editor between standard and source edit mode.
26435 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26437 toggleSourceEdit : function(sourceEditMode)
26439 this.editorcore.toggleSourceEdit(sourceEditMode);
26441 if(this.editorcore.sourceEditMode){
26442 Roo.log('editor - showing textarea');
26445 // Roo.log(this.syncValue());
26447 this.inputEl().removeClass(['hide', 'x-hidden']);
26448 this.inputEl().dom.removeAttribute('tabIndex');
26449 this.inputEl().focus();
26451 Roo.log('editor - hiding textarea');
26453 // Roo.log(this.pushValue());
26456 this.inputEl().addClass(['hide', 'x-hidden']);
26457 this.inputEl().dom.setAttribute('tabIndex', -1);
26458 //this.deferFocus();
26461 if(this.resizable){
26462 this.setSize(this.wrap.getSize());
26465 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26468 // private (for BoxComponent)
26469 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26471 // private (for BoxComponent)
26472 getResizeEl : function(){
26476 // private (for BoxComponent)
26477 getPositionEl : function(){
26482 initEvents : function(){
26483 this.originalValue = this.getValue();
26487 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26490 // markInvalid : Roo.emptyFn,
26492 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26495 // clearInvalid : Roo.emptyFn,
26497 setValue : function(v){
26498 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26499 this.editorcore.pushValue();
26504 deferFocus : function(){
26505 this.focus.defer(10, this);
26509 focus : function(){
26510 this.editorcore.focus();
26516 onDestroy : function(){
26522 for (var i =0; i < this.toolbars.length;i++) {
26523 // fixme - ask toolbars for heights?
26524 this.toolbars[i].onDestroy();
26527 this.wrap.dom.innerHTML = '';
26528 this.wrap.remove();
26533 onFirstFocus : function(){
26534 //Roo.log("onFirstFocus");
26535 this.editorcore.onFirstFocus();
26536 for (var i =0; i < this.toolbars.length;i++) {
26537 this.toolbars[i].onFirstFocus();
26543 syncValue : function()
26545 this.editorcore.syncValue();
26548 pushValue : function()
26550 this.editorcore.pushValue();
26554 // hide stuff that is not compatible
26568 * @event specialkey
26572 * @cfg {String} fieldClass @hide
26575 * @cfg {String} focusClass @hide
26578 * @cfg {String} autoCreate @hide
26581 * @cfg {String} inputType @hide
26585 * @cfg {String} invalidText @hide
26588 * @cfg {String} msgFx @hide
26591 * @cfg {String} validateOnBlur @hide
26600 Roo.namespace('Roo.bootstrap.htmleditor');
26602 * @class Roo.bootstrap.HtmlEditorToolbar1
26608 new Roo.bootstrap.HtmlEditor({
26611 new Roo.bootstrap.HtmlEditorToolbar1({
26612 disable : { fonts: 1 , format: 1, ..., ... , ...],
26618 * @cfg {Object} disable List of elements to disable..
26619 * @cfg {Array} btns List of additional buttons.
26623 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26626 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26629 Roo.apply(this, config);
26631 // default disabled, based on 'good practice'..
26632 this.disable = this.disable || {};
26633 Roo.applyIf(this.disable, {
26636 specialElements : true
26638 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26640 this.editor = config.editor;
26641 this.editorcore = config.editor.editorcore;
26643 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26645 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26646 // dont call parent... till later.
26648 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26653 editorcore : false,
26658 "h1","h2","h3","h4","h5","h6",
26660 "abbr", "acronym", "address", "cite", "samp", "var",
26664 onRender : function(ct, position)
26666 // Roo.log("Call onRender: " + this.xtype);
26668 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26670 this.el.dom.style.marginBottom = '0';
26672 var editorcore = this.editorcore;
26673 var editor= this.editor;
26676 var btn = function(id,cmd , toggle, handler, html){
26678 var event = toggle ? 'toggle' : 'click';
26683 xns: Roo.bootstrap,
26687 enableToggle:toggle !== false,
26689 pressed : toggle ? false : null,
26692 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26693 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26699 // var cb_box = function...
26704 xns: Roo.bootstrap,
26709 xns: Roo.bootstrap,
26713 Roo.each(this.formats, function(f) {
26714 style.menu.items.push({
26716 xns: Roo.bootstrap,
26717 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26722 editorcore.insertTag(this.tagname);
26729 children.push(style);
26731 btn('bold',false,true);
26732 btn('italic',false,true);
26733 btn('align-left', 'justifyleft',true);
26734 btn('align-center', 'justifycenter',true);
26735 btn('align-right' , 'justifyright',true);
26736 btn('link', false, false, function(btn) {
26737 //Roo.log("create link?");
26738 var url = prompt(this.createLinkText, this.defaultLinkValue);
26739 if(url && url != 'http:/'+'/'){
26740 this.editorcore.relayCmd('createlink', url);
26743 btn('list','insertunorderedlist',true);
26744 btn('pencil', false,true, function(btn){
26746 this.toggleSourceEdit(btn.pressed);
26749 if (this.editor.btns.length > 0) {
26750 for (var i = 0; i<this.editor.btns.length; i++) {
26751 children.push(this.editor.btns[i]);
26759 xns: Roo.bootstrap,
26764 xns: Roo.bootstrap,
26769 cog.menu.items.push({
26771 xns: Roo.bootstrap,
26772 html : Clean styles,
26777 editorcore.insertTag(this.tagname);
26786 this.xtype = 'NavSimplebar';
26788 for(var i=0;i< children.length;i++) {
26790 this.buttons.add(this.addxtypeChild(children[i]));
26794 editor.on('editorevent', this.updateToolbar, this);
26796 onBtnClick : function(id)
26798 this.editorcore.relayCmd(id);
26799 this.editorcore.focus();
26803 * Protected method that will not generally be called directly. It triggers
26804 * a toolbar update by reading the markup state of the current selection in the editor.
26806 updateToolbar: function(){
26808 if(!this.editorcore.activated){
26809 this.editor.onFirstFocus(); // is this neeed?
26813 var btns = this.buttons;
26814 var doc = this.editorcore.doc;
26815 btns.get('bold').setActive(doc.queryCommandState('bold'));
26816 btns.get('italic').setActive(doc.queryCommandState('italic'));
26817 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26819 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26820 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26821 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26823 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26824 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26827 var ans = this.editorcore.getAllAncestors();
26828 if (this.formatCombo) {
26831 var store = this.formatCombo.store;
26832 this.formatCombo.setValue("");
26833 for (var i =0; i < ans.length;i++) {
26834 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26836 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26844 // hides menus... - so this cant be on a menu...
26845 Roo.bootstrap.MenuMgr.hideAll();
26847 Roo.bootstrap.MenuMgr.hideAll();
26848 //this.editorsyncValue();
26850 onFirstFocus: function() {
26851 this.buttons.each(function(item){
26855 toggleSourceEdit : function(sourceEditMode){
26858 if(sourceEditMode){
26859 Roo.log("disabling buttons");
26860 this.buttons.each( function(item){
26861 if(item.cmd != 'pencil'){
26867 Roo.log("enabling buttons");
26868 if(this.editorcore.initialized){
26869 this.buttons.each( function(item){
26875 Roo.log("calling toggole on editor");
26876 // tell the editor that it's been pressed..
26877 this.editor.toggleSourceEdit(sourceEditMode);
26891 * @class Roo.bootstrap.Markdown
26892 * @extends Roo.bootstrap.TextArea
26893 * Bootstrap Showdown editable area
26894 * @cfg {string} content
26897 * Create a new Showdown
26900 Roo.bootstrap.Markdown = function(config){
26901 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26905 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26909 initEvents : function()
26912 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26913 this.markdownEl = this.el.createChild({
26914 cls : 'roo-markdown-area'
26916 this.inputEl().addClass('d-none');
26917 if (this.getValue() == '') {
26918 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26921 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26923 this.markdownEl.on('click', this.toggleTextEdit, this);
26924 this.on('blur', this.toggleTextEdit, this);
26925 this.on('specialkey', this.resizeTextArea, this);
26928 toggleTextEdit : function()
26930 var sh = this.markdownEl.getHeight();
26931 this.inputEl().addClass('d-none');
26932 this.markdownEl.addClass('d-none');
26933 if (!this.editing) {
26935 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26936 this.inputEl().removeClass('d-none');
26937 this.inputEl().focus();
26938 this.editing = true;
26941 // show showdown...
26942 this.updateMarkdown();
26943 this.markdownEl.removeClass('d-none');
26944 this.editing = false;
26947 updateMarkdown : function()
26949 if (this.getValue() == '') {
26950 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26954 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26957 resizeTextArea: function () {
26960 Roo.log([sh, this.getValue().split("\n").length * 30]);
26961 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26963 setValue : function(val)
26965 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26966 if (!this.editing) {
26967 this.updateMarkdown();
26973 if (!this.editing) {
26974 this.toggleTextEdit();
26982 * @class Roo.bootstrap.Table.AbstractSelectionModel
26983 * @extends Roo.util.Observable
26984 * Abstract base class for grid SelectionModels. It provides the interface that should be
26985 * implemented by descendant classes. This class should not be directly instantiated.
26988 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26989 this.locked = false;
26990 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26994 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26995 /** @ignore Called by the grid automatically. Do not call directly. */
26996 init : function(grid){
27002 * Locks the selections.
27005 this.locked = true;
27009 * Unlocks the selections.
27011 unlock : function(){
27012 this.locked = false;
27016 * Returns true if the selections are locked.
27017 * @return {Boolean}
27019 isLocked : function(){
27020 return this.locked;
27024 initEvents : function ()
27030 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27031 * @class Roo.bootstrap.Table.RowSelectionModel
27032 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27033 * It supports multiple selections and keyboard selection/navigation.
27035 * @param {Object} config
27038 Roo.bootstrap.Table.RowSelectionModel = function(config){
27039 Roo.apply(this, config);
27040 this.selections = new Roo.util.MixedCollection(false, function(o){
27045 this.lastActive = false;
27049 * @event selectionchange
27050 * Fires when the selection changes
27051 * @param {SelectionModel} this
27053 "selectionchange" : true,
27055 * @event afterselectionchange
27056 * Fires after the selection changes (eg. by key press or clicking)
27057 * @param {SelectionModel} this
27059 "afterselectionchange" : true,
27061 * @event beforerowselect
27062 * Fires when a row is selected being selected, return false to cancel.
27063 * @param {SelectionModel} this
27064 * @param {Number} rowIndex The selected index
27065 * @param {Boolean} keepExisting False if other selections will be cleared
27067 "beforerowselect" : true,
27070 * Fires when a row is selected.
27071 * @param {SelectionModel} this
27072 * @param {Number} rowIndex The selected index
27073 * @param {Roo.data.Record} r The record
27075 "rowselect" : true,
27077 * @event rowdeselect
27078 * Fires when a row is deselected.
27079 * @param {SelectionModel} this
27080 * @param {Number} rowIndex The selected index
27082 "rowdeselect" : true
27084 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27085 this.locked = false;
27088 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27090 * @cfg {Boolean} singleSelect
27091 * True to allow selection of only one row at a time (defaults to false)
27093 singleSelect : false,
27096 initEvents : function()
27099 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27100 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27101 //}else{ // allow click to work like normal
27102 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27104 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27105 this.grid.on("rowclick", this.handleMouseDown, this);
27107 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27108 "up" : function(e){
27110 this.selectPrevious(e.shiftKey);
27111 }else if(this.last !== false && this.lastActive !== false){
27112 var last = this.last;
27113 this.selectRange(this.last, this.lastActive-1);
27114 this.grid.getView().focusRow(this.lastActive);
27115 if(last !== false){
27119 this.selectFirstRow();
27121 this.fireEvent("afterselectionchange", this);
27123 "down" : function(e){
27125 this.selectNext(e.shiftKey);
27126 }else if(this.last !== false && this.lastActive !== false){
27127 var last = this.last;
27128 this.selectRange(this.last, this.lastActive+1);
27129 this.grid.getView().focusRow(this.lastActive);
27130 if(last !== false){
27134 this.selectFirstRow();
27136 this.fireEvent("afterselectionchange", this);
27140 this.grid.store.on('load', function(){
27141 this.selections.clear();
27144 var view = this.grid.view;
27145 view.on("refresh", this.onRefresh, this);
27146 view.on("rowupdated", this.onRowUpdated, this);
27147 view.on("rowremoved", this.onRemove, this);
27152 onRefresh : function()
27154 var ds = this.grid.store, i, v = this.grid.view;
27155 var s = this.selections;
27156 s.each(function(r){
27157 if((i = ds.indexOfId(r.id)) != -1){
27166 onRemove : function(v, index, r){
27167 this.selections.remove(r);
27171 onRowUpdated : function(v, index, r){
27172 if(this.isSelected(r)){
27173 v.onRowSelect(index);
27179 * @param {Array} records The records to select
27180 * @param {Boolean} keepExisting (optional) True to keep existing selections
27182 selectRecords : function(records, keepExisting)
27185 this.clearSelections();
27187 var ds = this.grid.store;
27188 for(var i = 0, len = records.length; i < len; i++){
27189 this.selectRow(ds.indexOf(records[i]), true);
27194 * Gets the number of selected rows.
27197 getCount : function(){
27198 return this.selections.length;
27202 * Selects the first row in the grid.
27204 selectFirstRow : function(){
27209 * Select the last row.
27210 * @param {Boolean} keepExisting (optional) True to keep existing selections
27212 selectLastRow : function(keepExisting){
27213 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27214 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27218 * Selects the row immediately following the last selected row.
27219 * @param {Boolean} keepExisting (optional) True to keep existing selections
27221 selectNext : function(keepExisting)
27223 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27224 this.selectRow(this.last+1, keepExisting);
27225 this.grid.getView().focusRow(this.last);
27230 * Selects the row that precedes the last selected row.
27231 * @param {Boolean} keepExisting (optional) True to keep existing selections
27233 selectPrevious : function(keepExisting){
27235 this.selectRow(this.last-1, keepExisting);
27236 this.grid.getView().focusRow(this.last);
27241 * Returns the selected records
27242 * @return {Array} Array of selected records
27244 getSelections : function(){
27245 return [].concat(this.selections.items);
27249 * Returns the first selected record.
27252 getSelected : function(){
27253 return this.selections.itemAt(0);
27258 * Clears all selections.
27260 clearSelections : function(fast)
27266 var ds = this.grid.store;
27267 var s = this.selections;
27268 s.each(function(r){
27269 this.deselectRow(ds.indexOfId(r.id));
27273 this.selections.clear();
27280 * Selects all rows.
27282 selectAll : function(){
27286 this.selections.clear();
27287 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27288 this.selectRow(i, true);
27293 * Returns True if there is a selection.
27294 * @return {Boolean}
27296 hasSelection : function(){
27297 return this.selections.length > 0;
27301 * Returns True if the specified row is selected.
27302 * @param {Number/Record} record The record or index of the record to check
27303 * @return {Boolean}
27305 isSelected : function(index){
27306 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27307 return (r && this.selections.key(r.id) ? true : false);
27311 * Returns True if the specified record id is selected.
27312 * @param {String} id The id of record to check
27313 * @return {Boolean}
27315 isIdSelected : function(id){
27316 return (this.selections.key(id) ? true : false);
27321 handleMouseDBClick : function(e, t){
27325 handleMouseDown : function(e, t)
27327 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27328 if(this.isLocked() || rowIndex < 0 ){
27331 if(e.shiftKey && this.last !== false){
27332 var last = this.last;
27333 this.selectRange(last, rowIndex, e.ctrlKey);
27334 this.last = last; // reset the last
27338 var isSelected = this.isSelected(rowIndex);
27339 //Roo.log("select row:" + rowIndex);
27341 this.deselectRow(rowIndex);
27343 this.selectRow(rowIndex, true);
27347 if(e.button !== 0 && isSelected){
27348 alert('rowIndex 2: ' + rowIndex);
27349 view.focusRow(rowIndex);
27350 }else if(e.ctrlKey && isSelected){
27351 this.deselectRow(rowIndex);
27352 }else if(!isSelected){
27353 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27354 view.focusRow(rowIndex);
27358 this.fireEvent("afterselectionchange", this);
27361 handleDragableRowClick : function(grid, rowIndex, e)
27363 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27364 this.selectRow(rowIndex, false);
27365 grid.view.focusRow(rowIndex);
27366 this.fireEvent("afterselectionchange", this);
27371 * Selects multiple rows.
27372 * @param {Array} rows Array of the indexes of the row to select
27373 * @param {Boolean} keepExisting (optional) True to keep existing selections
27375 selectRows : function(rows, keepExisting){
27377 this.clearSelections();
27379 for(var i = 0, len = rows.length; i < len; i++){
27380 this.selectRow(rows[i], true);
27385 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27386 * @param {Number} startRow The index of the first row in the range
27387 * @param {Number} endRow The index of the last row in the range
27388 * @param {Boolean} keepExisting (optional) True to retain existing selections
27390 selectRange : function(startRow, endRow, keepExisting){
27395 this.clearSelections();
27397 if(startRow <= endRow){
27398 for(var i = startRow; i <= endRow; i++){
27399 this.selectRow(i, true);
27402 for(var i = startRow; i >= endRow; i--){
27403 this.selectRow(i, true);
27409 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27410 * @param {Number} startRow The index of the first row in the range
27411 * @param {Number} endRow The index of the last row in the range
27413 deselectRange : function(startRow, endRow, preventViewNotify){
27417 for(var i = startRow; i <= endRow; i++){
27418 this.deselectRow(i, preventViewNotify);
27424 * @param {Number} row The index of the row to select
27425 * @param {Boolean} keepExisting (optional) True to keep existing selections
27427 selectRow : function(index, keepExisting, preventViewNotify)
27429 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27432 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27433 if(!keepExisting || this.singleSelect){
27434 this.clearSelections();
27437 var r = this.grid.store.getAt(index);
27438 //console.log('selectRow - record id :' + r.id);
27440 this.selections.add(r);
27441 this.last = this.lastActive = index;
27442 if(!preventViewNotify){
27443 var proxy = new Roo.Element(
27444 this.grid.getRowDom(index)
27446 proxy.addClass('bg-info info');
27448 this.fireEvent("rowselect", this, index, r);
27449 this.fireEvent("selectionchange", this);
27455 * @param {Number} row The index of the row to deselect
27457 deselectRow : function(index, preventViewNotify)
27462 if(this.last == index){
27465 if(this.lastActive == index){
27466 this.lastActive = false;
27469 var r = this.grid.store.getAt(index);
27474 this.selections.remove(r);
27475 //.console.log('deselectRow - record id :' + r.id);
27476 if(!preventViewNotify){
27478 var proxy = new Roo.Element(
27479 this.grid.getRowDom(index)
27481 proxy.removeClass('bg-info info');
27483 this.fireEvent("rowdeselect", this, index);
27484 this.fireEvent("selectionchange", this);
27488 restoreLast : function(){
27490 this.last = this._last;
27495 acceptsNav : function(row, col, cm){
27496 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27500 onEditorKey : function(field, e){
27501 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27506 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27508 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27510 }else if(k == e.ENTER && !e.ctrlKey){
27514 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27516 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27518 }else if(k == e.ESC){
27522 g.startEditing(newCell[0], newCell[1]);
27528 * Ext JS Library 1.1.1
27529 * Copyright(c) 2006-2007, Ext JS, LLC.
27531 * Originally Released Under LGPL - original licence link has changed is not relivant.
27534 * <script type="text/javascript">
27538 * @class Roo.bootstrap.PagingToolbar
27539 * @extends Roo.bootstrap.NavSimplebar
27540 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27542 * Create a new PagingToolbar
27543 * @param {Object} config The config object
27544 * @param {Roo.data.Store} store
27546 Roo.bootstrap.PagingToolbar = function(config)
27548 // old args format still supported... - xtype is prefered..
27549 // created from xtype...
27551 this.ds = config.dataSource;
27553 if (config.store && !this.ds) {
27554 this.store= Roo.factory(config.store, Roo.data);
27555 this.ds = this.store;
27556 this.ds.xmodule = this.xmodule || false;
27559 this.toolbarItems = [];
27560 if (config.items) {
27561 this.toolbarItems = config.items;
27564 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27569 this.bind(this.ds);
27572 if (Roo.bootstrap.version == 4) {
27573 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27575 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27580 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27582 * @cfg {Roo.data.Store} dataSource
27583 * The underlying data store providing the paged data
27586 * @cfg {String/HTMLElement/Element} container
27587 * container The id or element that will contain the toolbar
27590 * @cfg {Boolean} displayInfo
27591 * True to display the displayMsg (defaults to false)
27594 * @cfg {Number} pageSize
27595 * The number of records to display per page (defaults to 20)
27599 * @cfg {String} displayMsg
27600 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27602 displayMsg : 'Displaying {0} - {1} of {2}',
27604 * @cfg {String} emptyMsg
27605 * The message to display when no records are found (defaults to "No data to display")
27607 emptyMsg : 'No data to display',
27609 * Customizable piece of the default paging text (defaults to "Page")
27612 beforePageText : "Page",
27614 * Customizable piece of the default paging text (defaults to "of %0")
27617 afterPageText : "of {0}",
27619 * Customizable piece of the default paging text (defaults to "First Page")
27622 firstText : "First Page",
27624 * Customizable piece of the default paging text (defaults to "Previous Page")
27627 prevText : "Previous Page",
27629 * Customizable piece of the default paging text (defaults to "Next Page")
27632 nextText : "Next Page",
27634 * Customizable piece of the default paging text (defaults to "Last Page")
27637 lastText : "Last Page",
27639 * Customizable piece of the default paging text (defaults to "Refresh")
27642 refreshText : "Refresh",
27646 onRender : function(ct, position)
27648 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27649 this.navgroup.parentId = this.id;
27650 this.navgroup.onRender(this.el, null);
27651 // add the buttons to the navgroup
27653 if(this.displayInfo){
27654 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27655 this.displayEl = this.el.select('.x-paging-info', true).first();
27656 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27657 // this.displayEl = navel.el.select('span',true).first();
27663 Roo.each(_this.buttons, function(e){ // this might need to use render????
27664 Roo.factory(e).render(_this.el);
27668 Roo.each(_this.toolbarItems, function(e) {
27669 _this.navgroup.addItem(e);
27673 this.first = this.navgroup.addItem({
27674 tooltip: this.firstText,
27675 cls: "prev btn-outline-secondary",
27676 html : ' <i class="fa fa-step-backward"></i>',
27678 preventDefault: true,
27679 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27682 this.prev = this.navgroup.addItem({
27683 tooltip: this.prevText,
27684 cls: "prev btn-outline-secondary",
27685 html : ' <i class="fa fa-backward"></i>',
27687 preventDefault: true,
27688 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27690 //this.addSeparator();
27693 var field = this.navgroup.addItem( {
27695 cls : 'x-paging-position btn-outline-secondary',
27697 html : this.beforePageText +
27698 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27699 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27702 this.field = field.el.select('input', true).first();
27703 this.field.on("keydown", this.onPagingKeydown, this);
27704 this.field.on("focus", function(){this.dom.select();});
27707 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27708 //this.field.setHeight(18);
27709 //this.addSeparator();
27710 this.next = this.navgroup.addItem({
27711 tooltip: this.nextText,
27712 cls: "next btn-outline-secondary",
27713 html : ' <i class="fa fa-forward"></i>',
27715 preventDefault: true,
27716 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27718 this.last = this.navgroup.addItem({
27719 tooltip: this.lastText,
27720 html : ' <i class="fa fa-step-forward"></i>',
27721 cls: "next btn-outline-secondary",
27723 preventDefault: true,
27724 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27726 //this.addSeparator();
27727 this.loading = this.navgroup.addItem({
27728 tooltip: this.refreshText,
27729 cls: "btn-outline-secondary",
27730 html : ' <i class="fa fa-refresh"></i>',
27731 preventDefault: true,
27732 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27738 updateInfo : function(){
27739 if(this.displayEl){
27740 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27741 var msg = count == 0 ?
27745 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27747 this.displayEl.update(msg);
27752 onLoad : function(ds, r, o)
27754 this.cursor = o.params && o.params.start ? o.params.start : 0;
27756 var d = this.getPageData(),
27761 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27762 this.field.dom.value = ap;
27763 this.first.setDisabled(ap == 1);
27764 this.prev.setDisabled(ap == 1);
27765 this.next.setDisabled(ap == ps);
27766 this.last.setDisabled(ap == ps);
27767 this.loading.enable();
27772 getPageData : function(){
27773 var total = this.ds.getTotalCount();
27776 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27777 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27782 onLoadError : function(){
27783 this.loading.enable();
27787 onPagingKeydown : function(e){
27788 var k = e.getKey();
27789 var d = this.getPageData();
27791 var v = this.field.dom.value, pageNum;
27792 if(!v || isNaN(pageNum = parseInt(v, 10))){
27793 this.field.dom.value = d.activePage;
27796 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27797 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27800 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))
27802 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27803 this.field.dom.value = pageNum;
27804 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27807 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27809 var v = this.field.dom.value, pageNum;
27810 var increment = (e.shiftKey) ? 10 : 1;
27811 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27814 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27815 this.field.dom.value = d.activePage;
27818 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27820 this.field.dom.value = parseInt(v, 10) + increment;
27821 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27822 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27829 beforeLoad : function(){
27831 this.loading.disable();
27836 onClick : function(which){
27845 ds.load({params:{start: 0, limit: this.pageSize}});
27848 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27851 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27854 var total = ds.getTotalCount();
27855 var extra = total % this.pageSize;
27856 var lastStart = extra ? (total - extra) : total-this.pageSize;
27857 ds.load({params:{start: lastStart, limit: this.pageSize}});
27860 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27866 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27867 * @param {Roo.data.Store} store The data store to unbind
27869 unbind : function(ds){
27870 ds.un("beforeload", this.beforeLoad, this);
27871 ds.un("load", this.onLoad, this);
27872 ds.un("loadexception", this.onLoadError, this);
27873 ds.un("remove", this.updateInfo, this);
27874 ds.un("add", this.updateInfo, this);
27875 this.ds = undefined;
27879 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27880 * @param {Roo.data.Store} store The data store to bind
27882 bind : function(ds){
27883 ds.on("beforeload", this.beforeLoad, this);
27884 ds.on("load", this.onLoad, this);
27885 ds.on("loadexception", this.onLoadError, this);
27886 ds.on("remove", this.updateInfo, this);
27887 ds.on("add", this.updateInfo, this);
27898 * @class Roo.bootstrap.MessageBar
27899 * @extends Roo.bootstrap.Component
27900 * Bootstrap MessageBar class
27901 * @cfg {String} html contents of the MessageBar
27902 * @cfg {String} weight (info | success | warning | danger) default info
27903 * @cfg {String} beforeClass insert the bar before the given class
27904 * @cfg {Boolean} closable (true | false) default false
27905 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27908 * Create a new Element
27909 * @param {Object} config The config object
27912 Roo.bootstrap.MessageBar = function(config){
27913 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27916 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27922 beforeClass: 'bootstrap-sticky-wrap',
27924 getAutoCreate : function(){
27928 cls: 'alert alert-dismissable alert-' + this.weight,
27933 html: this.html || ''
27939 cfg.cls += ' alert-messages-fixed';
27953 onRender : function(ct, position)
27955 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27958 var cfg = Roo.apply({}, this.getAutoCreate());
27962 cfg.cls += ' ' + this.cls;
27965 cfg.style = this.style;
27967 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27969 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27972 this.el.select('>button.close').on('click', this.hide, this);
27978 if (!this.rendered) {
27984 this.fireEvent('show', this);
27990 if (!this.rendered) {
27996 this.fireEvent('hide', this);
27999 update : function()
28001 // var e = this.el.dom.firstChild;
28003 // if(this.closable){
28004 // e = e.nextSibling;
28007 // e.data = this.html || '';
28009 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28025 * @class Roo.bootstrap.Graph
28026 * @extends Roo.bootstrap.Component
28027 * Bootstrap Graph class
28031 @cfg {String} graphtype bar | vbar | pie
28032 @cfg {number} g_x coodinator | centre x (pie)
28033 @cfg {number} g_y coodinator | centre y (pie)
28034 @cfg {number} g_r radius (pie)
28035 @cfg {number} g_height height of the chart (respected by all elements in the set)
28036 @cfg {number} g_width width of the chart (respected by all elements in the set)
28037 @cfg {Object} title The title of the chart
28040 -opts (object) options for the chart
28042 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28043 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28045 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.
28046 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28048 o stretch (boolean)
28050 -opts (object) options for the pie
28053 o startAngle (number)
28054 o endAngle (number)
28058 * Create a new Input
28059 * @param {Object} config The config object
28062 Roo.bootstrap.Graph = function(config){
28063 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28069 * The img click event for the img.
28070 * @param {Roo.EventObject} e
28076 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28087 //g_colors: this.colors,
28094 getAutoCreate : function(){
28105 onRender : function(ct,position){
28108 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28110 if (typeof(Raphael) == 'undefined') {
28111 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28115 this.raphael = Raphael(this.el.dom);
28117 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28118 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28119 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28120 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28122 r.text(160, 10, "Single Series Chart").attr(txtattr);
28123 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28124 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28125 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28127 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28128 r.barchart(330, 10, 300, 220, data1);
28129 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28130 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28133 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28134 // r.barchart(30, 30, 560, 250, xdata, {
28135 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28136 // axis : "0 0 1 1",
28137 // axisxlabels : xdata
28138 // //yvalues : cols,
28141 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28143 // this.load(null,xdata,{
28144 // axis : "0 0 1 1",
28145 // axisxlabels : xdata
28150 load : function(graphtype,xdata,opts)
28152 this.raphael.clear();
28154 graphtype = this.graphtype;
28159 var r = this.raphael,
28160 fin = function () {
28161 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28163 fout = function () {
28164 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28166 pfin = function() {
28167 this.sector.stop();
28168 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28171 this.label[0].stop();
28172 this.label[0].attr({ r: 7.5 });
28173 this.label[1].attr({ "font-weight": 800 });
28176 pfout = function() {
28177 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28180 this.label[0].animate({ r: 5 }, 500, "bounce");
28181 this.label[1].attr({ "font-weight": 400 });
28187 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28190 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28193 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28194 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28196 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28203 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28208 setTitle: function(o)
28213 initEvents: function() {
28216 this.el.on('click', this.onClick, this);
28220 onClick : function(e)
28222 Roo.log('img onclick');
28223 this.fireEvent('click', this, e);
28235 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28238 * @class Roo.bootstrap.dash.NumberBox
28239 * @extends Roo.bootstrap.Component
28240 * Bootstrap NumberBox class
28241 * @cfg {String} headline Box headline
28242 * @cfg {String} content Box content
28243 * @cfg {String} icon Box icon
28244 * @cfg {String} footer Footer text
28245 * @cfg {String} fhref Footer href
28248 * Create a new NumberBox
28249 * @param {Object} config The config object
28253 Roo.bootstrap.dash.NumberBox = function(config){
28254 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28258 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28267 getAutoCreate : function(){
28271 cls : 'small-box ',
28279 cls : 'roo-headline',
28280 html : this.headline
28284 cls : 'roo-content',
28285 html : this.content
28299 cls : 'ion ' + this.icon
28308 cls : 'small-box-footer',
28309 href : this.fhref || '#',
28313 cfg.cn.push(footer);
28320 onRender : function(ct,position){
28321 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28328 setHeadline: function (value)
28330 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28333 setFooter: function (value, href)
28335 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28338 this.el.select('a.small-box-footer',true).first().attr('href', href);
28343 setContent: function (value)
28345 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28348 initEvents: function()
28362 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28365 * @class Roo.bootstrap.dash.TabBox
28366 * @extends Roo.bootstrap.Component
28367 * Bootstrap TabBox class
28368 * @cfg {String} title Title of the TabBox
28369 * @cfg {String} icon Icon of the TabBox
28370 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28371 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28374 * Create a new TabBox
28375 * @param {Object} config The config object
28379 Roo.bootstrap.dash.TabBox = function(config){
28380 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28385 * When a pane is added
28386 * @param {Roo.bootstrap.dash.TabPane} pane
28390 * @event activatepane
28391 * When a pane is activated
28392 * @param {Roo.bootstrap.dash.TabPane} pane
28394 "activatepane" : true
28402 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28407 tabScrollable : false,
28409 getChildContainer : function()
28411 return this.el.select('.tab-content', true).first();
28414 getAutoCreate : function(){
28418 cls: 'pull-left header',
28426 cls: 'fa ' + this.icon
28432 cls: 'nav nav-tabs pull-right',
28438 if(this.tabScrollable){
28445 cls: 'nav nav-tabs pull-right',
28456 cls: 'nav-tabs-custom',
28461 cls: 'tab-content no-padding',
28469 initEvents : function()
28471 //Roo.log('add add pane handler');
28472 this.on('addpane', this.onAddPane, this);
28475 * Updates the box title
28476 * @param {String} html to set the title to.
28478 setTitle : function(value)
28480 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28482 onAddPane : function(pane)
28484 this.panes.push(pane);
28485 //Roo.log('addpane');
28487 // tabs are rendere left to right..
28488 if(!this.showtabs){
28492 var ctr = this.el.select('.nav-tabs', true).first();
28495 var existing = ctr.select('.nav-tab',true);
28496 var qty = existing.getCount();;
28499 var tab = ctr.createChild({
28501 cls : 'nav-tab' + (qty ? '' : ' active'),
28509 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28512 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28514 pane.el.addClass('active');
28519 onTabClick : function(ev,un,ob,pane)
28521 //Roo.log('tab - prev default');
28522 ev.preventDefault();
28525 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28526 pane.tab.addClass('active');
28527 //Roo.log(pane.title);
28528 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28529 // technically we should have a deactivate event.. but maybe add later.
28530 // and it should not de-activate the selected tab...
28531 this.fireEvent('activatepane', pane);
28532 pane.el.addClass('active');
28533 pane.fireEvent('activate');
28538 getActivePane : function()
28541 Roo.each(this.panes, function(p) {
28542 if(p.el.hasClass('active')){
28563 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28565 * @class Roo.bootstrap.TabPane
28566 * @extends Roo.bootstrap.Component
28567 * Bootstrap TabPane class
28568 * @cfg {Boolean} active (false | true) Default false
28569 * @cfg {String} title title of panel
28573 * Create a new TabPane
28574 * @param {Object} config The config object
28577 Roo.bootstrap.dash.TabPane = function(config){
28578 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28584 * When a pane is activated
28585 * @param {Roo.bootstrap.dash.TabPane} pane
28592 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28597 // the tabBox that this is attached to.
28600 getAutoCreate : function()
28608 cfg.cls += ' active';
28613 initEvents : function()
28615 //Roo.log('trigger add pane handler');
28616 this.parent().fireEvent('addpane', this)
28620 * Updates the tab title
28621 * @param {String} html to set the title to.
28623 setTitle: function(str)
28629 this.tab.select('a', true).first().dom.innerHTML = str;
28646 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28649 * @class Roo.bootstrap.menu.Menu
28650 * @extends Roo.bootstrap.Component
28651 * Bootstrap Menu class - container for Menu
28652 * @cfg {String} html Text of the menu
28653 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28654 * @cfg {String} icon Font awesome icon
28655 * @cfg {String} pos Menu align to (top | bottom) default bottom
28659 * Create a new Menu
28660 * @param {Object} config The config object
28664 Roo.bootstrap.menu.Menu = function(config){
28665 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28669 * @event beforeshow
28670 * Fires before this menu is displayed
28671 * @param {Roo.bootstrap.menu.Menu} this
28675 * @event beforehide
28676 * Fires before this menu is hidden
28677 * @param {Roo.bootstrap.menu.Menu} this
28682 * Fires after this menu is displayed
28683 * @param {Roo.bootstrap.menu.Menu} this
28688 * Fires after this menu is hidden
28689 * @param {Roo.bootstrap.menu.Menu} this
28694 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28695 * @param {Roo.bootstrap.menu.Menu} this
28696 * @param {Roo.EventObject} e
28703 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28707 weight : 'default',
28712 getChildContainer : function() {
28713 if(this.isSubMenu){
28717 return this.el.select('ul.dropdown-menu', true).first();
28720 getAutoCreate : function()
28725 cls : 'roo-menu-text',
28733 cls : 'fa ' + this.icon
28744 cls : 'dropdown-button btn btn-' + this.weight,
28749 cls : 'dropdown-toggle btn btn-' + this.weight,
28759 cls : 'dropdown-menu'
28765 if(this.pos == 'top'){
28766 cfg.cls += ' dropup';
28769 if(this.isSubMenu){
28772 cls : 'dropdown-menu'
28779 onRender : function(ct, position)
28781 this.isSubMenu = ct.hasClass('dropdown-submenu');
28783 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28786 initEvents : function()
28788 if(this.isSubMenu){
28792 this.hidden = true;
28794 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28795 this.triggerEl.on('click', this.onTriggerPress, this);
28797 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28798 this.buttonEl.on('click', this.onClick, this);
28804 if(this.isSubMenu){
28808 return this.el.select('ul.dropdown-menu', true).first();
28811 onClick : function(e)
28813 this.fireEvent("click", this, e);
28816 onTriggerPress : function(e)
28818 if (this.isVisible()) {
28825 isVisible : function(){
28826 return !this.hidden;
28831 this.fireEvent("beforeshow", this);
28833 this.hidden = false;
28834 this.el.addClass('open');
28836 Roo.get(document).on("mouseup", this.onMouseUp, this);
28838 this.fireEvent("show", this);
28845 this.fireEvent("beforehide", this);
28847 this.hidden = true;
28848 this.el.removeClass('open');
28850 Roo.get(document).un("mouseup", this.onMouseUp);
28852 this.fireEvent("hide", this);
28855 onMouseUp : function()
28869 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28872 * @class Roo.bootstrap.menu.Item
28873 * @extends Roo.bootstrap.Component
28874 * Bootstrap MenuItem class
28875 * @cfg {Boolean} submenu (true | false) default false
28876 * @cfg {String} html text of the item
28877 * @cfg {String} href the link
28878 * @cfg {Boolean} disable (true | false) default false
28879 * @cfg {Boolean} preventDefault (true | false) default true
28880 * @cfg {String} icon Font awesome icon
28881 * @cfg {String} pos Submenu align to (left | right) default right
28885 * Create a new Item
28886 * @param {Object} config The config object
28890 Roo.bootstrap.menu.Item = function(config){
28891 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28895 * Fires when the mouse is hovering over this menu
28896 * @param {Roo.bootstrap.menu.Item} this
28897 * @param {Roo.EventObject} e
28902 * Fires when the mouse exits this menu
28903 * @param {Roo.bootstrap.menu.Item} this
28904 * @param {Roo.EventObject} e
28910 * The raw click event for the entire grid.
28911 * @param {Roo.EventObject} e
28917 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28922 preventDefault: true,
28927 getAutoCreate : function()
28932 cls : 'roo-menu-item-text',
28940 cls : 'fa ' + this.icon
28949 href : this.href || '#',
28956 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28960 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28962 if(this.pos == 'left'){
28963 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28970 initEvents : function()
28972 this.el.on('mouseover', this.onMouseOver, this);
28973 this.el.on('mouseout', this.onMouseOut, this);
28975 this.el.select('a', true).first().on('click', this.onClick, this);
28979 onClick : function(e)
28981 if(this.preventDefault){
28982 e.preventDefault();
28985 this.fireEvent("click", this, e);
28988 onMouseOver : function(e)
28990 if(this.submenu && this.pos == 'left'){
28991 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28994 this.fireEvent("mouseover", this, e);
28997 onMouseOut : function(e)
28999 this.fireEvent("mouseout", this, e);
29011 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29014 * @class Roo.bootstrap.menu.Separator
29015 * @extends Roo.bootstrap.Component
29016 * Bootstrap Separator class
29019 * Create a new Separator
29020 * @param {Object} config The config object
29024 Roo.bootstrap.menu.Separator = function(config){
29025 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29028 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29030 getAutoCreate : function(){
29033 cls: 'dropdown-divider divider'
29051 * @class Roo.bootstrap.Tooltip
29052 * Bootstrap Tooltip class
29053 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29054 * to determine which dom element triggers the tooltip.
29056 * It needs to add support for additional attributes like tooltip-position
29059 * Create a new Toolti
29060 * @param {Object} config The config object
29063 Roo.bootstrap.Tooltip = function(config){
29064 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29066 this.alignment = Roo.bootstrap.Tooltip.alignment;
29068 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29069 this.alignment = config.alignment;
29074 Roo.apply(Roo.bootstrap.Tooltip, {
29076 * @function init initialize tooltip monitoring.
29080 currentTip : false,
29081 currentRegion : false,
29087 Roo.get(document).on('mouseover', this.enter ,this);
29088 Roo.get(document).on('mouseout', this.leave, this);
29091 this.currentTip = new Roo.bootstrap.Tooltip();
29094 enter : function(ev)
29096 var dom = ev.getTarget();
29098 //Roo.log(['enter',dom]);
29099 var el = Roo.fly(dom);
29100 if (this.currentEl) {
29102 //Roo.log(this.currentEl);
29103 //Roo.log(this.currentEl.contains(dom));
29104 if (this.currentEl == el) {
29107 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29113 if (this.currentTip.el) {
29114 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29118 if(!el || el.dom == document){
29124 if (!el.attr('tooltip')) {
29125 pel = el.findParent("[tooltip]");
29127 bindEl = Roo.get(pel);
29133 // you can not look for children, as if el is the body.. then everythign is the child..
29134 if (!pel && !el.attr('tooltip')) { //
29135 if (!el.select("[tooltip]").elements.length) {
29138 // is the mouse over this child...?
29139 bindEl = el.select("[tooltip]").first();
29140 var xy = ev.getXY();
29141 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29142 //Roo.log("not in region.");
29145 //Roo.log("child element over..");
29148 this.currentEl = el;
29149 this.currentTip.bind(bindEl);
29150 this.currentRegion = Roo.lib.Region.getRegion(dom);
29151 this.currentTip.enter();
29154 leave : function(ev)
29156 var dom = ev.getTarget();
29157 //Roo.log(['leave',dom]);
29158 if (!this.currentEl) {
29163 if (dom != this.currentEl.dom) {
29166 var xy = ev.getXY();
29167 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29170 // only activate leave if mouse cursor is outside... bounding box..
29175 if (this.currentTip) {
29176 this.currentTip.leave();
29178 //Roo.log('clear currentEl');
29179 this.currentEl = false;
29184 'left' : ['r-l', [-2,0], 'right'],
29185 'right' : ['l-r', [2,0], 'left'],
29186 'bottom' : ['t-b', [0,2], 'top'],
29187 'top' : [ 'b-t', [0,-2], 'bottom']
29193 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29198 delay : null, // can be { show : 300 , hide: 500}
29202 hoverState : null, //???
29204 placement : 'bottom',
29208 getAutoCreate : function(){
29215 cls : 'tooltip-arrow arrow'
29218 cls : 'tooltip-inner'
29225 bind : function(el)
29230 initEvents : function()
29232 this.arrowEl = this.el.select('.arrow', true).first();
29233 this.innerEl = this.el.select('.tooltip-inner', true).first();
29236 enter : function () {
29238 if (this.timeout != null) {
29239 clearTimeout(this.timeout);
29242 this.hoverState = 'in';
29243 //Roo.log("enter - show");
29244 if (!this.delay || !this.delay.show) {
29249 this.timeout = setTimeout(function () {
29250 if (_t.hoverState == 'in') {
29253 }, this.delay.show);
29257 clearTimeout(this.timeout);
29259 this.hoverState = 'out';
29260 if (!this.delay || !this.delay.hide) {
29266 this.timeout = setTimeout(function () {
29267 //Roo.log("leave - timeout");
29269 if (_t.hoverState == 'out') {
29271 Roo.bootstrap.Tooltip.currentEl = false;
29276 show : function (msg)
29279 this.render(document.body);
29282 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29284 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29286 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29288 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29289 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29291 var placement = typeof this.placement == 'function' ?
29292 this.placement.call(this, this.el, on_el) :
29295 var autoToken = /\s?auto?\s?/i;
29296 var autoPlace = autoToken.test(placement);
29298 placement = placement.replace(autoToken, '') || 'top';
29302 //this.el.setXY([0,0]);
29304 //this.el.dom.style.display='block';
29306 //this.el.appendTo(on_el);
29308 var p = this.getPosition();
29309 var box = this.el.getBox();
29315 var align = this.alignment[placement];
29317 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29319 if(placement == 'top' || placement == 'bottom'){
29321 placement = 'right';
29324 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29325 placement = 'left';
29328 var scroll = Roo.select('body', true).first().getScroll();
29330 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29334 align = this.alignment[placement];
29336 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29340 var elems = document.getElementsByTagName('div');
29341 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29342 for (var i = 0; i < elems.length; i++) {
29343 var zindex = Number.parseInt(
29344 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29347 if (zindex > highest) {
29354 this.el.dom.style.zIndex = highest;
29356 this.el.alignTo(this.bindEl, align[0],align[1]);
29357 //var arrow = this.el.select('.arrow',true).first();
29358 //arrow.set(align[2],
29360 this.el.addClass(placement);
29361 this.el.addClass("bs-tooltip-"+ placement);
29363 this.el.addClass('in fade show');
29365 this.hoverState = null;
29367 if (this.el.hasClass('fade')) {
29382 //this.el.setXY([0,0]);
29383 this.el.removeClass(['show', 'in']);
29399 * @class Roo.bootstrap.LocationPicker
29400 * @extends Roo.bootstrap.Component
29401 * Bootstrap LocationPicker class
29402 * @cfg {Number} latitude Position when init default 0
29403 * @cfg {Number} longitude Position when init default 0
29404 * @cfg {Number} zoom default 15
29405 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29406 * @cfg {Boolean} mapTypeControl default false
29407 * @cfg {Boolean} disableDoubleClickZoom default false
29408 * @cfg {Boolean} scrollwheel default true
29409 * @cfg {Boolean} streetViewControl default false
29410 * @cfg {Number} radius default 0
29411 * @cfg {String} locationName
29412 * @cfg {Boolean} draggable default true
29413 * @cfg {Boolean} enableAutocomplete default false
29414 * @cfg {Boolean} enableReverseGeocode default true
29415 * @cfg {String} markerTitle
29418 * Create a new LocationPicker
29419 * @param {Object} config The config object
29423 Roo.bootstrap.LocationPicker = function(config){
29425 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29430 * Fires when the picker initialized.
29431 * @param {Roo.bootstrap.LocationPicker} this
29432 * @param {Google Location} location
29436 * @event positionchanged
29437 * Fires when the picker position changed.
29438 * @param {Roo.bootstrap.LocationPicker} this
29439 * @param {Google Location} location
29441 positionchanged : true,
29444 * Fires when the map resize.
29445 * @param {Roo.bootstrap.LocationPicker} this
29450 * Fires when the map show.
29451 * @param {Roo.bootstrap.LocationPicker} this
29456 * Fires when the map hide.
29457 * @param {Roo.bootstrap.LocationPicker} this
29462 * Fires when click the map.
29463 * @param {Roo.bootstrap.LocationPicker} this
29464 * @param {Map event} e
29468 * @event mapRightClick
29469 * Fires when right click the map.
29470 * @param {Roo.bootstrap.LocationPicker} this
29471 * @param {Map event} e
29473 mapRightClick : true,
29475 * @event markerClick
29476 * Fires when click the marker.
29477 * @param {Roo.bootstrap.LocationPicker} this
29478 * @param {Map event} e
29480 markerClick : true,
29482 * @event markerRightClick
29483 * Fires when right click the marker.
29484 * @param {Roo.bootstrap.LocationPicker} this
29485 * @param {Map event} e
29487 markerRightClick : true,
29489 * @event OverlayViewDraw
29490 * Fires when OverlayView Draw
29491 * @param {Roo.bootstrap.LocationPicker} this
29493 OverlayViewDraw : true,
29495 * @event OverlayViewOnAdd
29496 * Fires when OverlayView Draw
29497 * @param {Roo.bootstrap.LocationPicker} this
29499 OverlayViewOnAdd : true,
29501 * @event OverlayViewOnRemove
29502 * Fires when OverlayView Draw
29503 * @param {Roo.bootstrap.LocationPicker} this
29505 OverlayViewOnRemove : true,
29507 * @event OverlayViewShow
29508 * Fires when OverlayView Draw
29509 * @param {Roo.bootstrap.LocationPicker} this
29510 * @param {Pixel} cpx
29512 OverlayViewShow : true,
29514 * @event OverlayViewHide
29515 * Fires when OverlayView Draw
29516 * @param {Roo.bootstrap.LocationPicker} this
29518 OverlayViewHide : true,
29520 * @event loadexception
29521 * Fires when load google lib failed.
29522 * @param {Roo.bootstrap.LocationPicker} this
29524 loadexception : true
29529 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29531 gMapContext: false,
29537 mapTypeControl: false,
29538 disableDoubleClickZoom: false,
29540 streetViewControl: false,
29544 enableAutocomplete: false,
29545 enableReverseGeocode: true,
29548 getAutoCreate: function()
29553 cls: 'roo-location-picker'
29559 initEvents: function(ct, position)
29561 if(!this.el.getWidth() || this.isApplied()){
29565 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29570 initial: function()
29572 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29573 this.fireEvent('loadexception', this);
29577 if(!this.mapTypeId){
29578 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29581 this.gMapContext = this.GMapContext();
29583 this.initOverlayView();
29585 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29589 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29590 _this.setPosition(_this.gMapContext.marker.position);
29593 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29594 _this.fireEvent('mapClick', this, event);
29598 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29599 _this.fireEvent('mapRightClick', this, event);
29603 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29604 _this.fireEvent('markerClick', this, event);
29608 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29609 _this.fireEvent('markerRightClick', this, event);
29613 this.setPosition(this.gMapContext.location);
29615 this.fireEvent('initial', this, this.gMapContext.location);
29618 initOverlayView: function()
29622 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29626 _this.fireEvent('OverlayViewDraw', _this);
29631 _this.fireEvent('OverlayViewOnAdd', _this);
29634 onRemove: function()
29636 _this.fireEvent('OverlayViewOnRemove', _this);
29639 show: function(cpx)
29641 _this.fireEvent('OverlayViewShow', _this, cpx);
29646 _this.fireEvent('OverlayViewHide', _this);
29652 fromLatLngToContainerPixel: function(event)
29654 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29657 isApplied: function()
29659 return this.getGmapContext() == false ? false : true;
29662 getGmapContext: function()
29664 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29667 GMapContext: function()
29669 var position = new google.maps.LatLng(this.latitude, this.longitude);
29671 var _map = new google.maps.Map(this.el.dom, {
29674 mapTypeId: this.mapTypeId,
29675 mapTypeControl: this.mapTypeControl,
29676 disableDoubleClickZoom: this.disableDoubleClickZoom,
29677 scrollwheel: this.scrollwheel,
29678 streetViewControl: this.streetViewControl,
29679 locationName: this.locationName,
29680 draggable: this.draggable,
29681 enableAutocomplete: this.enableAutocomplete,
29682 enableReverseGeocode: this.enableReverseGeocode
29685 var _marker = new google.maps.Marker({
29686 position: position,
29688 title: this.markerTitle,
29689 draggable: this.draggable
29696 location: position,
29697 radius: this.radius,
29698 locationName: this.locationName,
29699 addressComponents: {
29700 formatted_address: null,
29701 addressLine1: null,
29702 addressLine2: null,
29704 streetNumber: null,
29708 stateOrProvince: null
29711 domContainer: this.el.dom,
29712 geodecoder: new google.maps.Geocoder()
29716 drawCircle: function(center, radius, options)
29718 if (this.gMapContext.circle != null) {
29719 this.gMapContext.circle.setMap(null);
29723 options = Roo.apply({}, options, {
29724 strokeColor: "#0000FF",
29725 strokeOpacity: .35,
29727 fillColor: "#0000FF",
29731 options.map = this.gMapContext.map;
29732 options.radius = radius;
29733 options.center = center;
29734 this.gMapContext.circle = new google.maps.Circle(options);
29735 return this.gMapContext.circle;
29741 setPosition: function(location)
29743 this.gMapContext.location = location;
29744 this.gMapContext.marker.setPosition(location);
29745 this.gMapContext.map.panTo(location);
29746 this.drawCircle(location, this.gMapContext.radius, {});
29750 if (this.gMapContext.settings.enableReverseGeocode) {
29751 this.gMapContext.geodecoder.geocode({
29752 latLng: this.gMapContext.location
29753 }, function(results, status) {
29755 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29756 _this.gMapContext.locationName = results[0].formatted_address;
29757 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29759 _this.fireEvent('positionchanged', this, location);
29766 this.fireEvent('positionchanged', this, location);
29771 google.maps.event.trigger(this.gMapContext.map, "resize");
29773 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29775 this.fireEvent('resize', this);
29778 setPositionByLatLng: function(latitude, longitude)
29780 this.setPosition(new google.maps.LatLng(latitude, longitude));
29783 getCurrentPosition: function()
29786 latitude: this.gMapContext.location.lat(),
29787 longitude: this.gMapContext.location.lng()
29791 getAddressName: function()
29793 return this.gMapContext.locationName;
29796 getAddressComponents: function()
29798 return this.gMapContext.addressComponents;
29801 address_component_from_google_geocode: function(address_components)
29805 for (var i = 0; i < address_components.length; i++) {
29806 var component = address_components[i];
29807 if (component.types.indexOf("postal_code") >= 0) {
29808 result.postalCode = component.short_name;
29809 } else if (component.types.indexOf("street_number") >= 0) {
29810 result.streetNumber = component.short_name;
29811 } else if (component.types.indexOf("route") >= 0) {
29812 result.streetName = component.short_name;
29813 } else if (component.types.indexOf("neighborhood") >= 0) {
29814 result.city = component.short_name;
29815 } else if (component.types.indexOf("locality") >= 0) {
29816 result.city = component.short_name;
29817 } else if (component.types.indexOf("sublocality") >= 0) {
29818 result.district = component.short_name;
29819 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29820 result.stateOrProvince = component.short_name;
29821 } else if (component.types.indexOf("country") >= 0) {
29822 result.country = component.short_name;
29826 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29827 result.addressLine2 = "";
29831 setZoomLevel: function(zoom)
29833 this.gMapContext.map.setZoom(zoom);
29846 this.fireEvent('show', this);
29857 this.fireEvent('hide', this);
29862 Roo.apply(Roo.bootstrap.LocationPicker, {
29864 OverlayView : function(map, options)
29866 options = options || {};
29873 * @class Roo.bootstrap.Alert
29874 * @extends Roo.bootstrap.Component
29875 * Bootstrap Alert class - shows an alert area box
29877 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29878 Enter a valid email address
29881 * @cfg {String} title The title of alert
29882 * @cfg {String} html The content of alert
29883 * @cfg {String} weight ( success | info | warning | danger )
29884 * @cfg {String} fa font-awesomeicon
29885 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29886 * @cfg {Boolean} close true to show a x closer
29890 * Create a new alert
29891 * @param {Object} config The config object
29895 Roo.bootstrap.Alert = function(config){
29896 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29900 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29906 faicon: false, // BC
29910 getAutoCreate : function()
29922 style : this.close ? '' : 'display:none'
29926 cls : 'roo-alert-icon'
29931 cls : 'roo-alert-title',
29936 cls : 'roo-alert-text',
29943 cfg.cn[0].cls += ' fa ' + this.faicon;
29946 cfg.cn[0].cls += ' fa ' + this.fa;
29950 cfg.cls += ' alert-' + this.weight;
29956 initEvents: function()
29958 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29959 this.titleEl = this.el.select('.roo-alert-title',true).first();
29960 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29961 if (this.seconds > 0) {
29962 this.hide.defer(this.seconds, this);
29966 setTitle : function(str)
29968 this.titleEl.dom.innerHTML = str;
29971 setText : function(str)
29973 this.titleEl.dom.innerHTML = str;
29976 setWeight : function(weight)
29979 this.el.removeClass('alert-' + this.weight);
29982 this.weight = weight;
29984 this.el.addClass('alert-' + this.weight);
29987 setIcon : function(icon)
29990 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29993 this.faicon = icon;
29995 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30016 * @class Roo.bootstrap.UploadCropbox
30017 * @extends Roo.bootstrap.Component
30018 * Bootstrap UploadCropbox class
30019 * @cfg {String} emptyText show when image has been loaded
30020 * @cfg {String} rotateNotify show when image too small to rotate
30021 * @cfg {Number} errorTimeout default 3000
30022 * @cfg {Number} minWidth default 300
30023 * @cfg {Number} minHeight default 300
30024 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30025 * @cfg {Boolean} isDocument (true|false) default false
30026 * @cfg {String} url action url
30027 * @cfg {String} paramName default 'imageUpload'
30028 * @cfg {String} method default POST
30029 * @cfg {Boolean} loadMask (true|false) default true
30030 * @cfg {Boolean} loadingText default 'Loading...'
30033 * Create a new UploadCropbox
30034 * @param {Object} config The config object
30037 Roo.bootstrap.UploadCropbox = function(config){
30038 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30042 * @event beforeselectfile
30043 * Fire before select file
30044 * @param {Roo.bootstrap.UploadCropbox} this
30046 "beforeselectfile" : true,
30049 * Fire after initEvent
30050 * @param {Roo.bootstrap.UploadCropbox} this
30055 * Fire after initEvent
30056 * @param {Roo.bootstrap.UploadCropbox} this
30057 * @param {String} data
30062 * Fire when preparing the file data
30063 * @param {Roo.bootstrap.UploadCropbox} this
30064 * @param {Object} file
30069 * Fire when get exception
30070 * @param {Roo.bootstrap.UploadCropbox} this
30071 * @param {XMLHttpRequest} xhr
30073 "exception" : true,
30075 * @event beforeloadcanvas
30076 * Fire before load the canvas
30077 * @param {Roo.bootstrap.UploadCropbox} this
30078 * @param {String} src
30080 "beforeloadcanvas" : true,
30083 * Fire when trash image
30084 * @param {Roo.bootstrap.UploadCropbox} this
30089 * Fire when download the image
30090 * @param {Roo.bootstrap.UploadCropbox} this
30094 * @event footerbuttonclick
30095 * Fire when footerbuttonclick
30096 * @param {Roo.bootstrap.UploadCropbox} this
30097 * @param {String} type
30099 "footerbuttonclick" : true,
30103 * @param {Roo.bootstrap.UploadCropbox} this
30108 * Fire when rotate the image
30109 * @param {Roo.bootstrap.UploadCropbox} this
30110 * @param {String} pos
30115 * Fire when inspect the file
30116 * @param {Roo.bootstrap.UploadCropbox} this
30117 * @param {Object} file
30122 * Fire when xhr upload the file
30123 * @param {Roo.bootstrap.UploadCropbox} this
30124 * @param {Object} data
30129 * Fire when arrange the file data
30130 * @param {Roo.bootstrap.UploadCropbox} this
30131 * @param {Object} formData
30136 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30139 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30141 emptyText : 'Click to upload image',
30142 rotateNotify : 'Image is too small to rotate',
30143 errorTimeout : 3000,
30157 cropType : 'image/jpeg',
30159 canvasLoaded : false,
30160 isDocument : false,
30162 paramName : 'imageUpload',
30164 loadingText : 'Loading...',
30167 getAutoCreate : function()
30171 cls : 'roo-upload-cropbox',
30175 cls : 'roo-upload-cropbox-selector',
30180 cls : 'roo-upload-cropbox-body',
30181 style : 'cursor:pointer',
30185 cls : 'roo-upload-cropbox-preview'
30189 cls : 'roo-upload-cropbox-thumb'
30193 cls : 'roo-upload-cropbox-empty-notify',
30194 html : this.emptyText
30198 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30199 html : this.rotateNotify
30205 cls : 'roo-upload-cropbox-footer',
30208 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30218 onRender : function(ct, position)
30220 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30222 if (this.buttons.length) {
30224 Roo.each(this.buttons, function(bb) {
30226 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30228 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30234 this.maskEl = this.el;
30238 initEvents : function()
30240 this.urlAPI = (window.createObjectURL && window) ||
30241 (window.URL && URL.revokeObjectURL && URL) ||
30242 (window.webkitURL && webkitURL);
30244 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30245 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30247 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30248 this.selectorEl.hide();
30250 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30251 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30253 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30254 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30255 this.thumbEl.hide();
30257 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30258 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30260 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30261 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30262 this.errorEl.hide();
30264 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30265 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30266 this.footerEl.hide();
30268 this.setThumbBoxSize();
30274 this.fireEvent('initial', this);
30281 window.addEventListener("resize", function() { _this.resize(); } );
30283 this.bodyEl.on('click', this.beforeSelectFile, this);
30286 this.bodyEl.on('touchstart', this.onTouchStart, this);
30287 this.bodyEl.on('touchmove', this.onTouchMove, this);
30288 this.bodyEl.on('touchend', this.onTouchEnd, this);
30292 this.bodyEl.on('mousedown', this.onMouseDown, this);
30293 this.bodyEl.on('mousemove', this.onMouseMove, this);
30294 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30295 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30296 Roo.get(document).on('mouseup', this.onMouseUp, this);
30299 this.selectorEl.on('change', this.onFileSelected, this);
30305 this.baseScale = 1;
30307 this.baseRotate = 1;
30308 this.dragable = false;
30309 this.pinching = false;
30312 this.cropData = false;
30313 this.notifyEl.dom.innerHTML = this.emptyText;
30315 this.selectorEl.dom.value = '';
30319 resize : function()
30321 if(this.fireEvent('resize', this) != false){
30322 this.setThumbBoxPosition();
30323 this.setCanvasPosition();
30327 onFooterButtonClick : function(e, el, o, type)
30330 case 'rotate-left' :
30331 this.onRotateLeft(e);
30333 case 'rotate-right' :
30334 this.onRotateRight(e);
30337 this.beforeSelectFile(e);
30352 this.fireEvent('footerbuttonclick', this, type);
30355 beforeSelectFile : function(e)
30357 e.preventDefault();
30359 if(this.fireEvent('beforeselectfile', this) != false){
30360 this.selectorEl.dom.click();
30364 onFileSelected : function(e)
30366 e.preventDefault();
30368 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30372 var file = this.selectorEl.dom.files[0];
30374 if(this.fireEvent('inspect', this, file) != false){
30375 this.prepare(file);
30380 trash : function(e)
30382 this.fireEvent('trash', this);
30385 download : function(e)
30387 this.fireEvent('download', this);
30390 loadCanvas : function(src)
30392 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30396 this.imageEl = document.createElement('img');
30400 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30402 this.imageEl.src = src;
30406 onLoadCanvas : function()
30408 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30409 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30411 this.bodyEl.un('click', this.beforeSelectFile, this);
30413 this.notifyEl.hide();
30414 this.thumbEl.show();
30415 this.footerEl.show();
30417 this.baseRotateLevel();
30419 if(this.isDocument){
30420 this.setThumbBoxSize();
30423 this.setThumbBoxPosition();
30425 this.baseScaleLevel();
30431 this.canvasLoaded = true;
30434 this.maskEl.unmask();
30439 setCanvasPosition : function()
30441 if(!this.canvasEl){
30445 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30446 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30448 this.previewEl.setLeft(pw);
30449 this.previewEl.setTop(ph);
30453 onMouseDown : function(e)
30457 this.dragable = true;
30458 this.pinching = false;
30460 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30461 this.dragable = false;
30465 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30466 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30470 onMouseMove : function(e)
30474 if(!this.canvasLoaded){
30478 if (!this.dragable){
30482 var minX = Math.ceil(this.thumbEl.getLeft(true));
30483 var minY = Math.ceil(this.thumbEl.getTop(true));
30485 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30486 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30488 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30489 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30491 x = x - this.mouseX;
30492 y = y - this.mouseY;
30494 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30495 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30497 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30498 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30500 this.previewEl.setLeft(bgX);
30501 this.previewEl.setTop(bgY);
30503 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30504 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30507 onMouseUp : function(e)
30511 this.dragable = false;
30514 onMouseWheel : function(e)
30518 this.startScale = this.scale;
30520 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30522 if(!this.zoomable()){
30523 this.scale = this.startScale;
30532 zoomable : function()
30534 var minScale = this.thumbEl.getWidth() / this.minWidth;
30536 if(this.minWidth < this.minHeight){
30537 minScale = this.thumbEl.getHeight() / this.minHeight;
30540 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30541 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30545 (this.rotate == 0 || this.rotate == 180) &&
30547 width > this.imageEl.OriginWidth ||
30548 height > this.imageEl.OriginHeight ||
30549 (width < this.minWidth && height < this.minHeight)
30557 (this.rotate == 90 || this.rotate == 270) &&
30559 width > this.imageEl.OriginWidth ||
30560 height > this.imageEl.OriginHeight ||
30561 (width < this.minHeight && height < this.minWidth)
30568 !this.isDocument &&
30569 (this.rotate == 0 || this.rotate == 180) &&
30571 width < this.minWidth ||
30572 width > this.imageEl.OriginWidth ||
30573 height < this.minHeight ||
30574 height > this.imageEl.OriginHeight
30581 !this.isDocument &&
30582 (this.rotate == 90 || this.rotate == 270) &&
30584 width < this.minHeight ||
30585 width > this.imageEl.OriginWidth ||
30586 height < this.minWidth ||
30587 height > this.imageEl.OriginHeight
30597 onRotateLeft : function(e)
30599 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30601 var minScale = this.thumbEl.getWidth() / this.minWidth;
30603 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30604 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30606 this.startScale = this.scale;
30608 while (this.getScaleLevel() < minScale){
30610 this.scale = this.scale + 1;
30612 if(!this.zoomable()){
30617 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30618 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30623 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30630 this.scale = this.startScale;
30632 this.onRotateFail();
30637 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30639 if(this.isDocument){
30640 this.setThumbBoxSize();
30641 this.setThumbBoxPosition();
30642 this.setCanvasPosition();
30647 this.fireEvent('rotate', this, 'left');
30651 onRotateRight : function(e)
30653 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30655 var minScale = this.thumbEl.getWidth() / this.minWidth;
30657 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30658 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30660 this.startScale = this.scale;
30662 while (this.getScaleLevel() < minScale){
30664 this.scale = this.scale + 1;
30666 if(!this.zoomable()){
30671 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30672 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30677 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30684 this.scale = this.startScale;
30686 this.onRotateFail();
30691 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30693 if(this.isDocument){
30694 this.setThumbBoxSize();
30695 this.setThumbBoxPosition();
30696 this.setCanvasPosition();
30701 this.fireEvent('rotate', this, 'right');
30704 onRotateFail : function()
30706 this.errorEl.show(true);
30710 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30715 this.previewEl.dom.innerHTML = '';
30717 var canvasEl = document.createElement("canvas");
30719 var contextEl = canvasEl.getContext("2d");
30721 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30722 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30723 var center = this.imageEl.OriginWidth / 2;
30725 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30726 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30727 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30728 center = this.imageEl.OriginHeight / 2;
30731 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30733 contextEl.translate(center, center);
30734 contextEl.rotate(this.rotate * Math.PI / 180);
30736 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30738 this.canvasEl = document.createElement("canvas");
30740 this.contextEl = this.canvasEl.getContext("2d");
30742 switch (this.rotate) {
30745 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30746 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30748 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30753 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30754 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30756 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30757 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);
30761 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30766 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30767 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30769 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30770 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);
30774 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);
30779 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30780 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30782 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30783 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30787 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);
30794 this.previewEl.appendChild(this.canvasEl);
30796 this.setCanvasPosition();
30801 if(!this.canvasLoaded){
30805 var imageCanvas = document.createElement("canvas");
30807 var imageContext = imageCanvas.getContext("2d");
30809 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30810 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30812 var center = imageCanvas.width / 2;
30814 imageContext.translate(center, center);
30816 imageContext.rotate(this.rotate * Math.PI / 180);
30818 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30820 var canvas = document.createElement("canvas");
30822 var context = canvas.getContext("2d");
30824 canvas.width = this.minWidth;
30825 canvas.height = this.minHeight;
30827 switch (this.rotate) {
30830 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30831 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30833 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30834 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30836 var targetWidth = this.minWidth - 2 * x;
30837 var targetHeight = this.minHeight - 2 * y;
30841 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30842 scale = targetWidth / width;
30845 if(x > 0 && y == 0){
30846 scale = targetHeight / height;
30849 if(x > 0 && y > 0){
30850 scale = targetWidth / width;
30852 if(width < height){
30853 scale = targetHeight / height;
30857 context.scale(scale, scale);
30859 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30860 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30862 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30863 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30865 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30870 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30871 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30873 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30874 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30876 var targetWidth = this.minWidth - 2 * x;
30877 var targetHeight = this.minHeight - 2 * y;
30881 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30882 scale = targetWidth / width;
30885 if(x > 0 && y == 0){
30886 scale = targetHeight / height;
30889 if(x > 0 && y > 0){
30890 scale = targetWidth / width;
30892 if(width < height){
30893 scale = targetHeight / height;
30897 context.scale(scale, scale);
30899 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30900 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30902 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30903 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30905 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30907 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30912 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30913 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30915 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30916 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30918 var targetWidth = this.minWidth - 2 * x;
30919 var targetHeight = this.minHeight - 2 * y;
30923 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30924 scale = targetWidth / width;
30927 if(x > 0 && y == 0){
30928 scale = targetHeight / height;
30931 if(x > 0 && y > 0){
30932 scale = targetWidth / width;
30934 if(width < height){
30935 scale = targetHeight / height;
30939 context.scale(scale, scale);
30941 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30942 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30944 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30945 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30947 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30948 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30950 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30955 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30956 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30958 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30959 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30961 var targetWidth = this.minWidth - 2 * x;
30962 var targetHeight = this.minHeight - 2 * y;
30966 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30967 scale = targetWidth / width;
30970 if(x > 0 && y == 0){
30971 scale = targetHeight / height;
30974 if(x > 0 && y > 0){
30975 scale = targetWidth / width;
30977 if(width < height){
30978 scale = targetHeight / height;
30982 context.scale(scale, scale);
30984 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30985 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30987 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30988 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30990 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30992 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30999 this.cropData = canvas.toDataURL(this.cropType);
31001 if(this.fireEvent('crop', this, this.cropData) !== false){
31002 this.process(this.file, this.cropData);
31009 setThumbBoxSize : function()
31013 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31014 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31015 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31017 this.minWidth = width;
31018 this.minHeight = height;
31020 if(this.rotate == 90 || this.rotate == 270){
31021 this.minWidth = height;
31022 this.minHeight = width;
31027 width = Math.ceil(this.minWidth * height / this.minHeight);
31029 if(this.minWidth > this.minHeight){
31031 height = Math.ceil(this.minHeight * width / this.minWidth);
31034 this.thumbEl.setStyle({
31035 width : width + 'px',
31036 height : height + 'px'
31043 setThumbBoxPosition : function()
31045 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31046 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31048 this.thumbEl.setLeft(x);
31049 this.thumbEl.setTop(y);
31053 baseRotateLevel : function()
31055 this.baseRotate = 1;
31058 typeof(this.exif) != 'undefined' &&
31059 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31060 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31062 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31065 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31069 baseScaleLevel : function()
31073 if(this.isDocument){
31075 if(this.baseRotate == 6 || this.baseRotate == 8){
31077 height = this.thumbEl.getHeight();
31078 this.baseScale = height / this.imageEl.OriginWidth;
31080 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31081 width = this.thumbEl.getWidth();
31082 this.baseScale = width / this.imageEl.OriginHeight;
31088 height = this.thumbEl.getHeight();
31089 this.baseScale = height / this.imageEl.OriginHeight;
31091 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31092 width = this.thumbEl.getWidth();
31093 this.baseScale = width / this.imageEl.OriginWidth;
31099 if(this.baseRotate == 6 || this.baseRotate == 8){
31101 width = this.thumbEl.getHeight();
31102 this.baseScale = width / this.imageEl.OriginHeight;
31104 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31105 height = this.thumbEl.getWidth();
31106 this.baseScale = height / this.imageEl.OriginHeight;
31109 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31110 height = this.thumbEl.getWidth();
31111 this.baseScale = height / this.imageEl.OriginHeight;
31113 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31114 width = this.thumbEl.getHeight();
31115 this.baseScale = width / this.imageEl.OriginWidth;
31122 width = this.thumbEl.getWidth();
31123 this.baseScale = width / this.imageEl.OriginWidth;
31125 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31126 height = this.thumbEl.getHeight();
31127 this.baseScale = height / this.imageEl.OriginHeight;
31130 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31132 height = this.thumbEl.getHeight();
31133 this.baseScale = height / this.imageEl.OriginHeight;
31135 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31136 width = this.thumbEl.getWidth();
31137 this.baseScale = width / this.imageEl.OriginWidth;
31145 getScaleLevel : function()
31147 return this.baseScale * Math.pow(1.1, this.scale);
31150 onTouchStart : function(e)
31152 if(!this.canvasLoaded){
31153 this.beforeSelectFile(e);
31157 var touches = e.browserEvent.touches;
31163 if(touches.length == 1){
31164 this.onMouseDown(e);
31168 if(touches.length != 2){
31174 for(var i = 0, finger; finger = touches[i]; i++){
31175 coords.push(finger.pageX, finger.pageY);
31178 var x = Math.pow(coords[0] - coords[2], 2);
31179 var y = Math.pow(coords[1] - coords[3], 2);
31181 this.startDistance = Math.sqrt(x + y);
31183 this.startScale = this.scale;
31185 this.pinching = true;
31186 this.dragable = false;
31190 onTouchMove : function(e)
31192 if(!this.pinching && !this.dragable){
31196 var touches = e.browserEvent.touches;
31203 this.onMouseMove(e);
31209 for(var i = 0, finger; finger = touches[i]; i++){
31210 coords.push(finger.pageX, finger.pageY);
31213 var x = Math.pow(coords[0] - coords[2], 2);
31214 var y = Math.pow(coords[1] - coords[3], 2);
31216 this.endDistance = Math.sqrt(x + y);
31218 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31220 if(!this.zoomable()){
31221 this.scale = this.startScale;
31229 onTouchEnd : function(e)
31231 this.pinching = false;
31232 this.dragable = false;
31236 process : function(file, crop)
31239 this.maskEl.mask(this.loadingText);
31242 this.xhr = new XMLHttpRequest();
31244 file.xhr = this.xhr;
31246 this.xhr.open(this.method, this.url, true);
31249 "Accept": "application/json",
31250 "Cache-Control": "no-cache",
31251 "X-Requested-With": "XMLHttpRequest"
31254 for (var headerName in headers) {
31255 var headerValue = headers[headerName];
31257 this.xhr.setRequestHeader(headerName, headerValue);
31263 this.xhr.onload = function()
31265 _this.xhrOnLoad(_this.xhr);
31268 this.xhr.onerror = function()
31270 _this.xhrOnError(_this.xhr);
31273 var formData = new FormData();
31275 formData.append('returnHTML', 'NO');
31278 formData.append('crop', crop);
31281 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31282 formData.append(this.paramName, file, file.name);
31285 if(typeof(file.filename) != 'undefined'){
31286 formData.append('filename', file.filename);
31289 if(typeof(file.mimetype) != 'undefined'){
31290 formData.append('mimetype', file.mimetype);
31293 if(this.fireEvent('arrange', this, formData) != false){
31294 this.xhr.send(formData);
31298 xhrOnLoad : function(xhr)
31301 this.maskEl.unmask();
31304 if (xhr.readyState !== 4) {
31305 this.fireEvent('exception', this, xhr);
31309 var response = Roo.decode(xhr.responseText);
31311 if(!response.success){
31312 this.fireEvent('exception', this, xhr);
31316 var response = Roo.decode(xhr.responseText);
31318 this.fireEvent('upload', this, response);
31322 xhrOnError : function()
31325 this.maskEl.unmask();
31328 Roo.log('xhr on error');
31330 var response = Roo.decode(xhr.responseText);
31336 prepare : function(file)
31339 this.maskEl.mask(this.loadingText);
31345 if(typeof(file) === 'string'){
31346 this.loadCanvas(file);
31350 if(!file || !this.urlAPI){
31355 this.cropType = file.type;
31359 if(this.fireEvent('prepare', this, this.file) != false){
31361 var reader = new FileReader();
31363 reader.onload = function (e) {
31364 if (e.target.error) {
31365 Roo.log(e.target.error);
31369 var buffer = e.target.result,
31370 dataView = new DataView(buffer),
31372 maxOffset = dataView.byteLength - 4,
31376 if (dataView.getUint16(0) === 0xffd8) {
31377 while (offset < maxOffset) {
31378 markerBytes = dataView.getUint16(offset);
31380 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31381 markerLength = dataView.getUint16(offset + 2) + 2;
31382 if (offset + markerLength > dataView.byteLength) {
31383 Roo.log('Invalid meta data: Invalid segment size.');
31387 if(markerBytes == 0xffe1){
31388 _this.parseExifData(
31395 offset += markerLength;
31405 var url = _this.urlAPI.createObjectURL(_this.file);
31407 _this.loadCanvas(url);
31412 reader.readAsArrayBuffer(this.file);
31418 parseExifData : function(dataView, offset, length)
31420 var tiffOffset = offset + 10,
31424 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31425 // No Exif data, might be XMP data instead
31429 // Check for the ASCII code for "Exif" (0x45786966):
31430 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31431 // No Exif data, might be XMP data instead
31434 if (tiffOffset + 8 > dataView.byteLength) {
31435 Roo.log('Invalid Exif data: Invalid segment size.');
31438 // Check for the two null bytes:
31439 if (dataView.getUint16(offset + 8) !== 0x0000) {
31440 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31443 // Check the byte alignment:
31444 switch (dataView.getUint16(tiffOffset)) {
31446 littleEndian = true;
31449 littleEndian = false;
31452 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31455 // Check for the TIFF tag marker (0x002A):
31456 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31457 Roo.log('Invalid Exif data: Missing TIFF marker.');
31460 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31461 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31463 this.parseExifTags(
31466 tiffOffset + dirOffset,
31471 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31476 if (dirOffset + 6 > dataView.byteLength) {
31477 Roo.log('Invalid Exif data: Invalid directory offset.');
31480 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31481 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31482 if (dirEndOffset + 4 > dataView.byteLength) {
31483 Roo.log('Invalid Exif data: Invalid directory size.');
31486 for (i = 0; i < tagsNumber; i += 1) {
31490 dirOffset + 2 + 12 * i, // tag offset
31494 // Return the offset to the next directory:
31495 return dataView.getUint32(dirEndOffset, littleEndian);
31498 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31500 var tag = dataView.getUint16(offset, littleEndian);
31502 this.exif[tag] = this.getExifValue(
31506 dataView.getUint16(offset + 2, littleEndian), // tag type
31507 dataView.getUint32(offset + 4, littleEndian), // tag length
31512 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31514 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31523 Roo.log('Invalid Exif data: Invalid tag type.');
31527 tagSize = tagType.size * length;
31528 // Determine if the value is contained in the dataOffset bytes,
31529 // or if the value at the dataOffset is a pointer to the actual data:
31530 dataOffset = tagSize > 4 ?
31531 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31532 if (dataOffset + tagSize > dataView.byteLength) {
31533 Roo.log('Invalid Exif data: Invalid data offset.');
31536 if (length === 1) {
31537 return tagType.getValue(dataView, dataOffset, littleEndian);
31540 for (i = 0; i < length; i += 1) {
31541 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31544 if (tagType.ascii) {
31546 // Concatenate the chars:
31547 for (i = 0; i < values.length; i += 1) {
31549 // Ignore the terminating NULL byte(s):
31550 if (c === '\u0000') {
31562 Roo.apply(Roo.bootstrap.UploadCropbox, {
31564 'Orientation': 0x0112
31568 1: 0, //'top-left',
31570 3: 180, //'bottom-right',
31571 // 4: 'bottom-left',
31573 6: 90, //'right-top',
31574 // 7: 'right-bottom',
31575 8: 270 //'left-bottom'
31579 // byte, 8-bit unsigned int:
31581 getValue: function (dataView, dataOffset) {
31582 return dataView.getUint8(dataOffset);
31586 // ascii, 8-bit byte:
31588 getValue: function (dataView, dataOffset) {
31589 return String.fromCharCode(dataView.getUint8(dataOffset));
31594 // short, 16 bit int:
31596 getValue: function (dataView, dataOffset, littleEndian) {
31597 return dataView.getUint16(dataOffset, littleEndian);
31601 // long, 32 bit int:
31603 getValue: function (dataView, dataOffset, littleEndian) {
31604 return dataView.getUint32(dataOffset, littleEndian);
31608 // rational = two long values, first is numerator, second is denominator:
31610 getValue: function (dataView, dataOffset, littleEndian) {
31611 return dataView.getUint32(dataOffset, littleEndian) /
31612 dataView.getUint32(dataOffset + 4, littleEndian);
31616 // slong, 32 bit signed int:
31618 getValue: function (dataView, dataOffset, littleEndian) {
31619 return dataView.getInt32(dataOffset, littleEndian);
31623 // srational, two slongs, first is numerator, second is denominator:
31625 getValue: function (dataView, dataOffset, littleEndian) {
31626 return dataView.getInt32(dataOffset, littleEndian) /
31627 dataView.getInt32(dataOffset + 4, littleEndian);
31637 cls : 'btn-group roo-upload-cropbox-rotate-left',
31638 action : 'rotate-left',
31642 cls : 'btn btn-default',
31643 html : '<i class="fa fa-undo"></i>'
31649 cls : 'btn-group roo-upload-cropbox-picture',
31650 action : 'picture',
31654 cls : 'btn btn-default',
31655 html : '<i class="fa fa-picture-o"></i>'
31661 cls : 'btn-group roo-upload-cropbox-rotate-right',
31662 action : 'rotate-right',
31666 cls : 'btn btn-default',
31667 html : '<i class="fa fa-repeat"></i>'
31675 cls : 'btn-group roo-upload-cropbox-rotate-left',
31676 action : 'rotate-left',
31680 cls : 'btn btn-default',
31681 html : '<i class="fa fa-undo"></i>'
31687 cls : 'btn-group roo-upload-cropbox-download',
31688 action : 'download',
31692 cls : 'btn btn-default',
31693 html : '<i class="fa fa-download"></i>'
31699 cls : 'btn-group roo-upload-cropbox-crop',
31704 cls : 'btn btn-default',
31705 html : '<i class="fa fa-crop"></i>'
31711 cls : 'btn-group roo-upload-cropbox-trash',
31716 cls : 'btn btn-default',
31717 html : '<i class="fa fa-trash"></i>'
31723 cls : 'btn-group roo-upload-cropbox-rotate-right',
31724 action : 'rotate-right',
31728 cls : 'btn btn-default',
31729 html : '<i class="fa fa-repeat"></i>'
31737 cls : 'btn-group roo-upload-cropbox-rotate-left',
31738 action : 'rotate-left',
31742 cls : 'btn btn-default',
31743 html : '<i class="fa fa-undo"></i>'
31749 cls : 'btn-group roo-upload-cropbox-rotate-right',
31750 action : 'rotate-right',
31754 cls : 'btn btn-default',
31755 html : '<i class="fa fa-repeat"></i>'
31768 * @class Roo.bootstrap.DocumentManager
31769 * @extends Roo.bootstrap.Component
31770 * Bootstrap DocumentManager class
31771 * @cfg {String} paramName default 'imageUpload'
31772 * @cfg {String} toolTipName default 'filename'
31773 * @cfg {String} method default POST
31774 * @cfg {String} url action url
31775 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31776 * @cfg {Boolean} multiple multiple upload default true
31777 * @cfg {Number} thumbSize default 300
31778 * @cfg {String} fieldLabel
31779 * @cfg {Number} labelWidth default 4
31780 * @cfg {String} labelAlign (left|top) default left
31781 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31782 * @cfg {Number} labellg set the width of label (1-12)
31783 * @cfg {Number} labelmd set the width of label (1-12)
31784 * @cfg {Number} labelsm set the width of label (1-12)
31785 * @cfg {Number} labelxs set the width of label (1-12)
31788 * Create a new DocumentManager
31789 * @param {Object} config The config object
31792 Roo.bootstrap.DocumentManager = function(config){
31793 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31796 this.delegates = [];
31801 * Fire when initial the DocumentManager
31802 * @param {Roo.bootstrap.DocumentManager} this
31807 * inspect selected file
31808 * @param {Roo.bootstrap.DocumentManager} this
31809 * @param {File} file
31814 * Fire when xhr load exception
31815 * @param {Roo.bootstrap.DocumentManager} this
31816 * @param {XMLHttpRequest} xhr
31818 "exception" : true,
31820 * @event afterupload
31821 * Fire when xhr load exception
31822 * @param {Roo.bootstrap.DocumentManager} this
31823 * @param {XMLHttpRequest} xhr
31825 "afterupload" : true,
31828 * prepare the form data
31829 * @param {Roo.bootstrap.DocumentManager} this
31830 * @param {Object} formData
31835 * Fire when remove the file
31836 * @param {Roo.bootstrap.DocumentManager} this
31837 * @param {Object} file
31842 * Fire after refresh the file
31843 * @param {Roo.bootstrap.DocumentManager} this
31848 * Fire after click the image
31849 * @param {Roo.bootstrap.DocumentManager} this
31850 * @param {Object} file
31855 * Fire when upload a image and editable set to true
31856 * @param {Roo.bootstrap.DocumentManager} this
31857 * @param {Object} file
31861 * @event beforeselectfile
31862 * Fire before select file
31863 * @param {Roo.bootstrap.DocumentManager} this
31865 "beforeselectfile" : true,
31868 * Fire before process file
31869 * @param {Roo.bootstrap.DocumentManager} this
31870 * @param {Object} file
31874 * @event previewrendered
31875 * Fire when preview rendered
31876 * @param {Roo.bootstrap.DocumentManager} this
31877 * @param {Object} file
31879 "previewrendered" : true,
31882 "previewResize" : true
31887 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31896 paramName : 'imageUpload',
31897 toolTipName : 'filename',
31900 labelAlign : 'left',
31910 getAutoCreate : function()
31912 var managerWidget = {
31914 cls : 'roo-document-manager',
31918 cls : 'roo-document-manager-selector',
31923 cls : 'roo-document-manager-uploader',
31927 cls : 'roo-document-manager-upload-btn',
31928 html : '<i class="fa fa-plus"></i>'
31939 cls : 'column col-md-12',
31944 if(this.fieldLabel.length){
31949 cls : 'column col-md-12',
31950 html : this.fieldLabel
31954 cls : 'column col-md-12',
31959 if(this.labelAlign == 'left'){
31964 html : this.fieldLabel
31973 if(this.labelWidth > 12){
31974 content[0].style = "width: " + this.labelWidth + 'px';
31977 if(this.labelWidth < 13 && this.labelmd == 0){
31978 this.labelmd = this.labelWidth;
31981 if(this.labellg > 0){
31982 content[0].cls += ' col-lg-' + this.labellg;
31983 content[1].cls += ' col-lg-' + (12 - this.labellg);
31986 if(this.labelmd > 0){
31987 content[0].cls += ' col-md-' + this.labelmd;
31988 content[1].cls += ' col-md-' + (12 - this.labelmd);
31991 if(this.labelsm > 0){
31992 content[0].cls += ' col-sm-' + this.labelsm;
31993 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31996 if(this.labelxs > 0){
31997 content[0].cls += ' col-xs-' + this.labelxs;
31998 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32006 cls : 'row clearfix',
32014 initEvents : function()
32016 this.managerEl = this.el.select('.roo-document-manager', true).first();
32017 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32019 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32020 this.selectorEl.hide();
32023 this.selectorEl.attr('multiple', 'multiple');
32026 this.selectorEl.on('change', this.onFileSelected, this);
32028 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32029 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32031 this.uploader.on('click', this.onUploaderClick, this);
32033 this.renderProgressDialog();
32037 window.addEventListener("resize", function() { _this.refresh(); } );
32039 this.fireEvent('initial', this);
32042 renderProgressDialog : function()
32046 this.progressDialog = new Roo.bootstrap.Modal({
32047 cls : 'roo-document-manager-progress-dialog',
32048 allow_close : false,
32059 btnclick : function() {
32060 _this.uploadCancel();
32066 this.progressDialog.render(Roo.get(document.body));
32068 this.progress = new Roo.bootstrap.Progress({
32069 cls : 'roo-document-manager-progress',
32074 this.progress.render(this.progressDialog.getChildContainer());
32076 this.progressBar = new Roo.bootstrap.ProgressBar({
32077 cls : 'roo-document-manager-progress-bar',
32080 aria_valuemax : 12,
32084 this.progressBar.render(this.progress.getChildContainer());
32087 onUploaderClick : function(e)
32089 e.preventDefault();
32091 if(this.fireEvent('beforeselectfile', this) != false){
32092 this.selectorEl.dom.click();
32097 onFileSelected : function(e)
32099 e.preventDefault();
32101 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32105 Roo.each(this.selectorEl.dom.files, function(file){
32106 if(this.fireEvent('inspect', this, file) != false){
32107 this.files.push(file);
32117 this.selectorEl.dom.value = '';
32119 if(!this.files || !this.files.length){
32123 if(this.boxes > 0 && this.files.length > this.boxes){
32124 this.files = this.files.slice(0, this.boxes);
32127 this.uploader.show();
32129 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32130 this.uploader.hide();
32139 Roo.each(this.files, function(file){
32141 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32142 var f = this.renderPreview(file);
32147 if(file.type.indexOf('image') != -1){
32148 this.delegates.push(
32150 _this.process(file);
32151 }).createDelegate(this)
32159 _this.process(file);
32160 }).createDelegate(this)
32165 this.files = files;
32167 this.delegates = this.delegates.concat(docs);
32169 if(!this.delegates.length){
32174 this.progressBar.aria_valuemax = this.delegates.length;
32181 arrange : function()
32183 if(!this.delegates.length){
32184 this.progressDialog.hide();
32189 var delegate = this.delegates.shift();
32191 this.progressDialog.show();
32193 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32195 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32200 refresh : function()
32202 this.uploader.show();
32204 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32205 this.uploader.hide();
32208 Roo.isTouch ? this.closable(false) : this.closable(true);
32210 this.fireEvent('refresh', this);
32213 onRemove : function(e, el, o)
32215 e.preventDefault();
32217 this.fireEvent('remove', this, o);
32221 remove : function(o)
32225 Roo.each(this.files, function(file){
32226 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32235 this.files = files;
32242 Roo.each(this.files, function(file){
32247 file.target.remove();
32256 onClick : function(e, el, o)
32258 e.preventDefault();
32260 this.fireEvent('click', this, o);
32264 closable : function(closable)
32266 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32268 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32280 xhrOnLoad : function(xhr)
32282 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32286 if (xhr.readyState !== 4) {
32288 this.fireEvent('exception', this, xhr);
32292 var response = Roo.decode(xhr.responseText);
32294 if(!response.success){
32296 this.fireEvent('exception', this, xhr);
32300 var file = this.renderPreview(response.data);
32302 this.files.push(file);
32306 this.fireEvent('afterupload', this, xhr);
32310 xhrOnError : function(xhr)
32312 Roo.log('xhr on error');
32314 var response = Roo.decode(xhr.responseText);
32321 process : function(file)
32323 if(this.fireEvent('process', this, file) !== false){
32324 if(this.editable && file.type.indexOf('image') != -1){
32325 this.fireEvent('edit', this, file);
32329 this.uploadStart(file, false);
32336 uploadStart : function(file, crop)
32338 this.xhr = new XMLHttpRequest();
32340 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32345 file.xhr = this.xhr;
32347 this.managerEl.createChild({
32349 cls : 'roo-document-manager-loading',
32353 tooltip : file.name,
32354 cls : 'roo-document-manager-thumb',
32355 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32361 this.xhr.open(this.method, this.url, true);
32364 "Accept": "application/json",
32365 "Cache-Control": "no-cache",
32366 "X-Requested-With": "XMLHttpRequest"
32369 for (var headerName in headers) {
32370 var headerValue = headers[headerName];
32372 this.xhr.setRequestHeader(headerName, headerValue);
32378 this.xhr.onload = function()
32380 _this.xhrOnLoad(_this.xhr);
32383 this.xhr.onerror = function()
32385 _this.xhrOnError(_this.xhr);
32388 var formData = new FormData();
32390 formData.append('returnHTML', 'NO');
32393 formData.append('crop', crop);
32396 formData.append(this.paramName, file, file.name);
32403 if(this.fireEvent('prepare', this, formData, options) != false){
32405 if(options.manually){
32409 this.xhr.send(formData);
32413 this.uploadCancel();
32416 uploadCancel : function()
32422 this.delegates = [];
32424 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32431 renderPreview : function(file)
32433 if(typeof(file.target) != 'undefined' && file.target){
32437 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32439 var previewEl = this.managerEl.createChild({
32441 cls : 'roo-document-manager-preview',
32445 tooltip : file[this.toolTipName],
32446 cls : 'roo-document-manager-thumb',
32447 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32452 html : '<i class="fa fa-times-circle"></i>'
32457 var close = previewEl.select('button.close', true).first();
32459 close.on('click', this.onRemove, this, file);
32461 file.target = previewEl;
32463 var image = previewEl.select('img', true).first();
32467 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32469 image.on('click', this.onClick, this, file);
32471 this.fireEvent('previewrendered', this, file);
32477 onPreviewLoad : function(file, image)
32479 if(typeof(file.target) == 'undefined' || !file.target){
32483 var width = image.dom.naturalWidth || image.dom.width;
32484 var height = image.dom.naturalHeight || image.dom.height;
32486 if(!this.previewResize) {
32490 if(width > height){
32491 file.target.addClass('wide');
32495 file.target.addClass('tall');
32500 uploadFromSource : function(file, crop)
32502 this.xhr = new XMLHttpRequest();
32504 this.managerEl.createChild({
32506 cls : 'roo-document-manager-loading',
32510 tooltip : file.name,
32511 cls : 'roo-document-manager-thumb',
32512 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32518 this.xhr.open(this.method, this.url, true);
32521 "Accept": "application/json",
32522 "Cache-Control": "no-cache",
32523 "X-Requested-With": "XMLHttpRequest"
32526 for (var headerName in headers) {
32527 var headerValue = headers[headerName];
32529 this.xhr.setRequestHeader(headerName, headerValue);
32535 this.xhr.onload = function()
32537 _this.xhrOnLoad(_this.xhr);
32540 this.xhr.onerror = function()
32542 _this.xhrOnError(_this.xhr);
32545 var formData = new FormData();
32547 formData.append('returnHTML', 'NO');
32549 formData.append('crop', crop);
32551 if(typeof(file.filename) != 'undefined'){
32552 formData.append('filename', file.filename);
32555 if(typeof(file.mimetype) != 'undefined'){
32556 formData.append('mimetype', file.mimetype);
32561 if(this.fireEvent('prepare', this, formData) != false){
32562 this.xhr.send(formData);
32572 * @class Roo.bootstrap.DocumentViewer
32573 * @extends Roo.bootstrap.Component
32574 * Bootstrap DocumentViewer class
32575 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32576 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32579 * Create a new DocumentViewer
32580 * @param {Object} config The config object
32583 Roo.bootstrap.DocumentViewer = function(config){
32584 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32589 * Fire after initEvent
32590 * @param {Roo.bootstrap.DocumentViewer} this
32596 * @param {Roo.bootstrap.DocumentViewer} this
32601 * Fire after download button
32602 * @param {Roo.bootstrap.DocumentViewer} this
32607 * Fire after trash button
32608 * @param {Roo.bootstrap.DocumentViewer} this
32615 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32617 showDownload : true,
32621 getAutoCreate : function()
32625 cls : 'roo-document-viewer',
32629 cls : 'roo-document-viewer-body',
32633 cls : 'roo-document-viewer-thumb',
32637 cls : 'roo-document-viewer-image'
32645 cls : 'roo-document-viewer-footer',
32648 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32652 cls : 'btn-group roo-document-viewer-download',
32656 cls : 'btn btn-default',
32657 html : '<i class="fa fa-download"></i>'
32663 cls : 'btn-group roo-document-viewer-trash',
32667 cls : 'btn btn-default',
32668 html : '<i class="fa fa-trash"></i>'
32681 initEvents : function()
32683 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32684 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32686 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32687 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32689 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32690 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32692 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32693 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32695 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32696 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32698 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32699 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32701 this.bodyEl.on('click', this.onClick, this);
32702 this.downloadBtn.on('click', this.onDownload, this);
32703 this.trashBtn.on('click', this.onTrash, this);
32705 this.downloadBtn.hide();
32706 this.trashBtn.hide();
32708 if(this.showDownload){
32709 this.downloadBtn.show();
32712 if(this.showTrash){
32713 this.trashBtn.show();
32716 if(!this.showDownload && !this.showTrash) {
32717 this.footerEl.hide();
32722 initial : function()
32724 this.fireEvent('initial', this);
32728 onClick : function(e)
32730 e.preventDefault();
32732 this.fireEvent('click', this);
32735 onDownload : function(e)
32737 e.preventDefault();
32739 this.fireEvent('download', this);
32742 onTrash : function(e)
32744 e.preventDefault();
32746 this.fireEvent('trash', this);
32758 * @class Roo.bootstrap.NavProgressBar
32759 * @extends Roo.bootstrap.Component
32760 * Bootstrap NavProgressBar class
32763 * Create a new nav progress bar
32764 * @param {Object} config The config object
32767 Roo.bootstrap.NavProgressBar = function(config){
32768 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32770 this.bullets = this.bullets || [];
32772 // Roo.bootstrap.NavProgressBar.register(this);
32776 * Fires when the active item changes
32777 * @param {Roo.bootstrap.NavProgressBar} this
32778 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32779 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32786 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32791 getAutoCreate : function()
32793 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32797 cls : 'roo-navigation-bar-group',
32801 cls : 'roo-navigation-top-bar'
32805 cls : 'roo-navigation-bullets-bar',
32809 cls : 'roo-navigation-bar'
32816 cls : 'roo-navigation-bottom-bar'
32826 initEvents: function()
32831 onRender : function(ct, position)
32833 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32835 if(this.bullets.length){
32836 Roo.each(this.bullets, function(b){
32845 addItem : function(cfg)
32847 var item = new Roo.bootstrap.NavProgressItem(cfg);
32849 item.parentId = this.id;
32850 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32853 var top = new Roo.bootstrap.Element({
32855 cls : 'roo-navigation-bar-text'
32858 var bottom = new Roo.bootstrap.Element({
32860 cls : 'roo-navigation-bar-text'
32863 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32864 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32866 var topText = new Roo.bootstrap.Element({
32868 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32871 var bottomText = new Roo.bootstrap.Element({
32873 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32876 topText.onRender(top.el, null);
32877 bottomText.onRender(bottom.el, null);
32880 item.bottomEl = bottom;
32883 this.barItems.push(item);
32888 getActive : function()
32890 var active = false;
32892 Roo.each(this.barItems, function(v){
32894 if (!v.isActive()) {
32906 setActiveItem : function(item)
32910 Roo.each(this.barItems, function(v){
32911 if (v.rid == item.rid) {
32915 if (v.isActive()) {
32916 v.setActive(false);
32921 item.setActive(true);
32923 this.fireEvent('changed', this, item, prev);
32926 getBarItem: function(rid)
32930 Roo.each(this.barItems, function(e) {
32931 if (e.rid != rid) {
32942 indexOfItem : function(item)
32946 Roo.each(this.barItems, function(v, i){
32948 if (v.rid != item.rid) {
32959 setActiveNext : function()
32961 var i = this.indexOfItem(this.getActive());
32963 if (i > this.barItems.length) {
32967 this.setActiveItem(this.barItems[i+1]);
32970 setActivePrev : function()
32972 var i = this.indexOfItem(this.getActive());
32978 this.setActiveItem(this.barItems[i-1]);
32981 format : function()
32983 if(!this.barItems.length){
32987 var width = 100 / this.barItems.length;
32989 Roo.each(this.barItems, function(i){
32990 i.el.setStyle('width', width + '%');
32991 i.topEl.el.setStyle('width', width + '%');
32992 i.bottomEl.el.setStyle('width', width + '%');
33001 * Nav Progress Item
33006 * @class Roo.bootstrap.NavProgressItem
33007 * @extends Roo.bootstrap.Component
33008 * Bootstrap NavProgressItem class
33009 * @cfg {String} rid the reference id
33010 * @cfg {Boolean} active (true|false) Is item active default false
33011 * @cfg {Boolean} disabled (true|false) Is item active default false
33012 * @cfg {String} html
33013 * @cfg {String} position (top|bottom) text position default bottom
33014 * @cfg {String} icon show icon instead of number
33017 * Create a new NavProgressItem
33018 * @param {Object} config The config object
33020 Roo.bootstrap.NavProgressItem = function(config){
33021 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33026 * The raw click event for the entire grid.
33027 * @param {Roo.bootstrap.NavProgressItem} this
33028 * @param {Roo.EventObject} e
33035 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33041 position : 'bottom',
33044 getAutoCreate : function()
33046 var iconCls = 'roo-navigation-bar-item-icon';
33048 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33052 cls: 'roo-navigation-bar-item',
33062 cfg.cls += ' active';
33065 cfg.cls += ' disabled';
33071 disable : function()
33073 this.setDisabled(true);
33076 enable : function()
33078 this.setDisabled(false);
33081 initEvents: function()
33083 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33085 this.iconEl.on('click', this.onClick, this);
33088 onClick : function(e)
33090 e.preventDefault();
33096 if(this.fireEvent('click', this, e) === false){
33100 this.parent().setActiveItem(this);
33103 isActive: function ()
33105 return this.active;
33108 setActive : function(state)
33110 if(this.active == state){
33114 this.active = state;
33117 this.el.addClass('active');
33121 this.el.removeClass('active');
33126 setDisabled : function(state)
33128 if(this.disabled == state){
33132 this.disabled = state;
33135 this.el.addClass('disabled');
33139 this.el.removeClass('disabled');
33142 tooltipEl : function()
33144 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33157 * @class Roo.bootstrap.FieldLabel
33158 * @extends Roo.bootstrap.Component
33159 * Bootstrap FieldLabel class
33160 * @cfg {String} html contents of the element
33161 * @cfg {String} tag tag of the element default label
33162 * @cfg {String} cls class of the element
33163 * @cfg {String} target label target
33164 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33165 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33166 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33167 * @cfg {String} iconTooltip default "This field is required"
33168 * @cfg {String} indicatorpos (left|right) default left
33171 * Create a new FieldLabel
33172 * @param {Object} config The config object
33175 Roo.bootstrap.FieldLabel = function(config){
33176 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33181 * Fires after the field has been marked as invalid.
33182 * @param {Roo.form.FieldLabel} this
33183 * @param {String} msg The validation message
33188 * Fires after the field has been validated with no errors.
33189 * @param {Roo.form.FieldLabel} this
33195 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33202 invalidClass : 'has-warning',
33203 validClass : 'has-success',
33204 iconTooltip : 'This field is required',
33205 indicatorpos : 'left',
33207 getAutoCreate : function(){
33210 if (!this.allowBlank) {
33216 cls : 'roo-bootstrap-field-label ' + this.cls,
33221 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33222 tooltip : this.iconTooltip
33231 if(this.indicatorpos == 'right'){
33234 cls : 'roo-bootstrap-field-label ' + this.cls,
33243 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33244 tooltip : this.iconTooltip
33253 initEvents: function()
33255 Roo.bootstrap.Element.superclass.initEvents.call(this);
33257 this.indicator = this.indicatorEl();
33259 if(this.indicator){
33260 this.indicator.removeClass('visible');
33261 this.indicator.addClass('invisible');
33264 Roo.bootstrap.FieldLabel.register(this);
33267 indicatorEl : function()
33269 var indicator = this.el.select('i.roo-required-indicator',true).first();
33280 * Mark this field as valid
33282 markValid : function()
33284 if(this.indicator){
33285 this.indicator.removeClass('visible');
33286 this.indicator.addClass('invisible');
33288 if (Roo.bootstrap.version == 3) {
33289 this.el.removeClass(this.invalidClass);
33290 this.el.addClass(this.validClass);
33292 this.el.removeClass('is-invalid');
33293 this.el.addClass('is-valid');
33297 this.fireEvent('valid', this);
33301 * Mark this field as invalid
33302 * @param {String} msg The validation message
33304 markInvalid : function(msg)
33306 if(this.indicator){
33307 this.indicator.removeClass('invisible');
33308 this.indicator.addClass('visible');
33310 if (Roo.bootstrap.version == 3) {
33311 this.el.removeClass(this.validClass);
33312 this.el.addClass(this.invalidClass);
33314 this.el.removeClass('is-valid');
33315 this.el.addClass('is-invalid');
33319 this.fireEvent('invalid', this, msg);
33325 Roo.apply(Roo.bootstrap.FieldLabel, {
33330 * register a FieldLabel Group
33331 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33333 register : function(label)
33335 if(this.groups.hasOwnProperty(label.target)){
33339 this.groups[label.target] = label;
33343 * fetch a FieldLabel Group based on the target
33344 * @param {string} target
33345 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33347 get: function(target) {
33348 if (typeof(this.groups[target]) == 'undefined') {
33352 return this.groups[target] ;
33361 * page DateSplitField.
33367 * @class Roo.bootstrap.DateSplitField
33368 * @extends Roo.bootstrap.Component
33369 * Bootstrap DateSplitField class
33370 * @cfg {string} fieldLabel - the label associated
33371 * @cfg {Number} labelWidth set the width of label (0-12)
33372 * @cfg {String} labelAlign (top|left)
33373 * @cfg {Boolean} dayAllowBlank (true|false) default false
33374 * @cfg {Boolean} monthAllowBlank (true|false) default false
33375 * @cfg {Boolean} yearAllowBlank (true|false) default false
33376 * @cfg {string} dayPlaceholder
33377 * @cfg {string} monthPlaceholder
33378 * @cfg {string} yearPlaceholder
33379 * @cfg {string} dayFormat default 'd'
33380 * @cfg {string} monthFormat default 'm'
33381 * @cfg {string} yearFormat default 'Y'
33382 * @cfg {Number} labellg set the width of label (1-12)
33383 * @cfg {Number} labelmd set the width of label (1-12)
33384 * @cfg {Number} labelsm set the width of label (1-12)
33385 * @cfg {Number} labelxs set the width of label (1-12)
33389 * Create a new DateSplitField
33390 * @param {Object} config The config object
33393 Roo.bootstrap.DateSplitField = function(config){
33394 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33400 * getting the data of years
33401 * @param {Roo.bootstrap.DateSplitField} this
33402 * @param {Object} years
33407 * getting the data of days
33408 * @param {Roo.bootstrap.DateSplitField} this
33409 * @param {Object} days
33414 * Fires after the field has been marked as invalid.
33415 * @param {Roo.form.Field} this
33416 * @param {String} msg The validation message
33421 * Fires after the field has been validated with no errors.
33422 * @param {Roo.form.Field} this
33428 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33431 labelAlign : 'top',
33433 dayAllowBlank : false,
33434 monthAllowBlank : false,
33435 yearAllowBlank : false,
33436 dayPlaceholder : '',
33437 monthPlaceholder : '',
33438 yearPlaceholder : '',
33442 isFormField : true,
33448 getAutoCreate : function()
33452 cls : 'row roo-date-split-field-group',
33457 cls : 'form-hidden-field roo-date-split-field-group-value',
33463 var labelCls = 'col-md-12';
33464 var contentCls = 'col-md-4';
33466 if(this.fieldLabel){
33470 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33474 html : this.fieldLabel
33479 if(this.labelAlign == 'left'){
33481 if(this.labelWidth > 12){
33482 label.style = "width: " + this.labelWidth + 'px';
33485 if(this.labelWidth < 13 && this.labelmd == 0){
33486 this.labelmd = this.labelWidth;
33489 if(this.labellg > 0){
33490 labelCls = ' col-lg-' + this.labellg;
33491 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33494 if(this.labelmd > 0){
33495 labelCls = ' col-md-' + this.labelmd;
33496 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33499 if(this.labelsm > 0){
33500 labelCls = ' col-sm-' + this.labelsm;
33501 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33504 if(this.labelxs > 0){
33505 labelCls = ' col-xs-' + this.labelxs;
33506 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33510 label.cls += ' ' + labelCls;
33512 cfg.cn.push(label);
33515 Roo.each(['day', 'month', 'year'], function(t){
33518 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33525 inputEl: function ()
33527 return this.el.select('.roo-date-split-field-group-value', true).first();
33530 onRender : function(ct, position)
33534 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33536 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33538 this.dayField = new Roo.bootstrap.ComboBox({
33539 allowBlank : this.dayAllowBlank,
33540 alwaysQuery : true,
33541 displayField : 'value',
33544 forceSelection : true,
33546 placeholder : this.dayPlaceholder,
33547 selectOnFocus : true,
33548 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33549 triggerAction : 'all',
33551 valueField : 'value',
33552 store : new Roo.data.SimpleStore({
33553 data : (function() {
33555 _this.fireEvent('days', _this, days);
33558 fields : [ 'value' ]
33561 select : function (_self, record, index)
33563 _this.setValue(_this.getValue());
33568 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33570 this.monthField = new Roo.bootstrap.MonthField({
33571 after : '<i class=\"fa fa-calendar\"></i>',
33572 allowBlank : this.monthAllowBlank,
33573 placeholder : this.monthPlaceholder,
33576 render : function (_self)
33578 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33579 e.preventDefault();
33583 select : function (_self, oldvalue, newvalue)
33585 _this.setValue(_this.getValue());
33590 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33592 this.yearField = new Roo.bootstrap.ComboBox({
33593 allowBlank : this.yearAllowBlank,
33594 alwaysQuery : true,
33595 displayField : 'value',
33598 forceSelection : true,
33600 placeholder : this.yearPlaceholder,
33601 selectOnFocus : true,
33602 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33603 triggerAction : 'all',
33605 valueField : 'value',
33606 store : new Roo.data.SimpleStore({
33607 data : (function() {
33609 _this.fireEvent('years', _this, years);
33612 fields : [ 'value' ]
33615 select : function (_self, record, index)
33617 _this.setValue(_this.getValue());
33622 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33625 setValue : function(v, format)
33627 this.inputEl.dom.value = v;
33629 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33631 var d = Date.parseDate(v, f);
33638 this.setDay(d.format(this.dayFormat));
33639 this.setMonth(d.format(this.monthFormat));
33640 this.setYear(d.format(this.yearFormat));
33647 setDay : function(v)
33649 this.dayField.setValue(v);
33650 this.inputEl.dom.value = this.getValue();
33655 setMonth : function(v)
33657 this.monthField.setValue(v, true);
33658 this.inputEl.dom.value = this.getValue();
33663 setYear : function(v)
33665 this.yearField.setValue(v);
33666 this.inputEl.dom.value = this.getValue();
33671 getDay : function()
33673 return this.dayField.getValue();
33676 getMonth : function()
33678 return this.monthField.getValue();
33681 getYear : function()
33683 return this.yearField.getValue();
33686 getValue : function()
33688 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33690 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33700 this.inputEl.dom.value = '';
33705 validate : function()
33707 var d = this.dayField.validate();
33708 var m = this.monthField.validate();
33709 var y = this.yearField.validate();
33714 (!this.dayAllowBlank && !d) ||
33715 (!this.monthAllowBlank && !m) ||
33716 (!this.yearAllowBlank && !y)
33721 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33730 this.markInvalid();
33735 markValid : function()
33738 var label = this.el.select('label', true).first();
33739 var icon = this.el.select('i.fa-star', true).first();
33745 this.fireEvent('valid', this);
33749 * Mark this field as invalid
33750 * @param {String} msg The validation message
33752 markInvalid : function(msg)
33755 var label = this.el.select('label', true).first();
33756 var icon = this.el.select('i.fa-star', true).first();
33758 if(label && !icon){
33759 this.el.select('.roo-date-split-field-label', true).createChild({
33761 cls : 'text-danger fa fa-lg fa-star',
33762 tooltip : 'This field is required',
33763 style : 'margin-right:5px;'
33767 this.fireEvent('invalid', this, msg);
33770 clearInvalid : function()
33772 var label = this.el.select('label', true).first();
33773 var icon = this.el.select('i.fa-star', true).first();
33779 this.fireEvent('valid', this);
33782 getName: function()
33792 * http://masonry.desandro.com
33794 * The idea is to render all the bricks based on vertical width...
33796 * The original code extends 'outlayer' - we might need to use that....
33802 * @class Roo.bootstrap.LayoutMasonry
33803 * @extends Roo.bootstrap.Component
33804 * Bootstrap Layout Masonry class
33807 * Create a new Element
33808 * @param {Object} config The config object
33811 Roo.bootstrap.LayoutMasonry = function(config){
33813 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33817 Roo.bootstrap.LayoutMasonry.register(this);
33823 * Fire after layout the items
33824 * @param {Roo.bootstrap.LayoutMasonry} this
33825 * @param {Roo.EventObject} e
33832 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33835 * @cfg {Boolean} isLayoutInstant = no animation?
33837 isLayoutInstant : false, // needed?
33840 * @cfg {Number} boxWidth width of the columns
33845 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33850 * @cfg {Number} padWidth padding below box..
33855 * @cfg {Number} gutter gutter width..
33860 * @cfg {Number} maxCols maximum number of columns
33866 * @cfg {Boolean} isAutoInitial defalut true
33868 isAutoInitial : true,
33873 * @cfg {Boolean} isHorizontal defalut false
33875 isHorizontal : false,
33877 currentSize : null,
33883 bricks: null, //CompositeElement
33887 _isLayoutInited : false,
33889 // isAlternative : false, // only use for vertical layout...
33892 * @cfg {Number} alternativePadWidth padding below box..
33894 alternativePadWidth : 50,
33896 selectedBrick : [],
33898 getAutoCreate : function(){
33900 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33904 cls: 'blog-masonary-wrapper ' + this.cls,
33906 cls : 'mas-boxes masonary'
33913 getChildContainer: function( )
33915 if (this.boxesEl) {
33916 return this.boxesEl;
33919 this.boxesEl = this.el.select('.mas-boxes').first();
33921 return this.boxesEl;
33925 initEvents : function()
33929 if(this.isAutoInitial){
33930 Roo.log('hook children rendered');
33931 this.on('childrenrendered', function() {
33932 Roo.log('children rendered');
33938 initial : function()
33940 this.selectedBrick = [];
33942 this.currentSize = this.el.getBox(true);
33944 Roo.EventManager.onWindowResize(this.resize, this);
33946 if(!this.isAutoInitial){
33954 //this.layout.defer(500,this);
33958 resize : function()
33960 var cs = this.el.getBox(true);
33963 this.currentSize.width == cs.width &&
33964 this.currentSize.x == cs.x &&
33965 this.currentSize.height == cs.height &&
33966 this.currentSize.y == cs.y
33968 Roo.log("no change in with or X or Y");
33972 this.currentSize = cs;
33978 layout : function()
33980 this._resetLayout();
33982 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33984 this.layoutItems( isInstant );
33986 this._isLayoutInited = true;
33988 this.fireEvent('layout', this);
33992 _resetLayout : function()
33994 if(this.isHorizontal){
33995 this.horizontalMeasureColumns();
33999 this.verticalMeasureColumns();
34003 verticalMeasureColumns : function()
34005 this.getContainerWidth();
34007 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34008 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34012 var boxWidth = this.boxWidth + this.padWidth;
34014 if(this.containerWidth < this.boxWidth){
34015 boxWidth = this.containerWidth
34018 var containerWidth = this.containerWidth;
34020 var cols = Math.floor(containerWidth / boxWidth);
34022 this.cols = Math.max( cols, 1 );
34024 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34026 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34028 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34030 this.colWidth = boxWidth + avail - this.padWidth;
34032 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34033 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34036 horizontalMeasureColumns : function()
34038 this.getContainerWidth();
34040 var boxWidth = this.boxWidth;
34042 if(this.containerWidth < boxWidth){
34043 boxWidth = this.containerWidth;
34046 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34048 this.el.setHeight(boxWidth);
34052 getContainerWidth : function()
34054 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34057 layoutItems : function( isInstant )
34059 Roo.log(this.bricks);
34061 var items = Roo.apply([], this.bricks);
34063 if(this.isHorizontal){
34064 this._horizontalLayoutItems( items , isInstant );
34068 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34069 // this._verticalAlternativeLayoutItems( items , isInstant );
34073 this._verticalLayoutItems( items , isInstant );
34077 _verticalLayoutItems : function ( items , isInstant)
34079 if ( !items || !items.length ) {
34084 ['xs', 'xs', 'xs', 'tall'],
34085 ['xs', 'xs', 'tall'],
34086 ['xs', 'xs', 'sm'],
34087 ['xs', 'xs', 'xs'],
34093 ['sm', 'xs', 'xs'],
34097 ['tall', 'xs', 'xs', 'xs'],
34098 ['tall', 'xs', 'xs'],
34110 Roo.each(items, function(item, k){
34112 switch (item.size) {
34113 // these layouts take up a full box,
34124 boxes.push([item]);
34147 var filterPattern = function(box, length)
34155 var pattern = box.slice(0, length);
34159 Roo.each(pattern, function(i){
34160 format.push(i.size);
34163 Roo.each(standard, function(s){
34165 if(String(s) != String(format)){
34174 if(!match && length == 1){
34179 filterPattern(box, length - 1);
34183 queue.push(pattern);
34185 box = box.slice(length, box.length);
34187 filterPattern(box, 4);
34193 Roo.each(boxes, function(box, k){
34199 if(box.length == 1){
34204 filterPattern(box, 4);
34208 this._processVerticalLayoutQueue( queue, isInstant );
34212 // _verticalAlternativeLayoutItems : function( items , isInstant )
34214 // if ( !items || !items.length ) {
34218 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34222 _horizontalLayoutItems : function ( items , isInstant)
34224 if ( !items || !items.length || items.length < 3) {
34230 var eItems = items.slice(0, 3);
34232 items = items.slice(3, items.length);
34235 ['xs', 'xs', 'xs', 'wide'],
34236 ['xs', 'xs', 'wide'],
34237 ['xs', 'xs', 'sm'],
34238 ['xs', 'xs', 'xs'],
34244 ['sm', 'xs', 'xs'],
34248 ['wide', 'xs', 'xs', 'xs'],
34249 ['wide', 'xs', 'xs'],
34262 Roo.each(items, function(item, k){
34264 switch (item.size) {
34275 boxes.push([item]);
34299 var filterPattern = function(box, length)
34307 var pattern = box.slice(0, length);
34311 Roo.each(pattern, function(i){
34312 format.push(i.size);
34315 Roo.each(standard, function(s){
34317 if(String(s) != String(format)){
34326 if(!match && length == 1){
34331 filterPattern(box, length - 1);
34335 queue.push(pattern);
34337 box = box.slice(length, box.length);
34339 filterPattern(box, 4);
34345 Roo.each(boxes, function(box, k){
34351 if(box.length == 1){
34356 filterPattern(box, 4);
34363 var pos = this.el.getBox(true);
34367 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34369 var hit_end = false;
34371 Roo.each(queue, function(box){
34375 Roo.each(box, function(b){
34377 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34387 Roo.each(box, function(b){
34389 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34392 mx = Math.max(mx, b.x);
34396 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34400 Roo.each(box, function(b){
34402 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34416 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34419 /** Sets position of item in DOM
34420 * @param {Element} item
34421 * @param {Number} x - horizontal position
34422 * @param {Number} y - vertical position
34423 * @param {Boolean} isInstant - disables transitions
34425 _processVerticalLayoutQueue : function( queue, isInstant )
34427 var pos = this.el.getBox(true);
34432 for (var i = 0; i < this.cols; i++){
34436 Roo.each(queue, function(box, k){
34438 var col = k % this.cols;
34440 Roo.each(box, function(b,kk){
34442 b.el.position('absolute');
34444 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34445 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34447 if(b.size == 'md-left' || b.size == 'md-right'){
34448 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34449 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34452 b.el.setWidth(width);
34453 b.el.setHeight(height);
34455 b.el.select('iframe',true).setSize(width,height);
34459 for (var i = 0; i < this.cols; i++){
34461 if(maxY[i] < maxY[col]){
34466 col = Math.min(col, i);
34470 x = pos.x + col * (this.colWidth + this.padWidth);
34474 var positions = [];
34476 switch (box.length){
34478 positions = this.getVerticalOneBoxColPositions(x, y, box);
34481 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34484 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34487 positions = this.getVerticalFourBoxColPositions(x, y, box);
34493 Roo.each(box, function(b,kk){
34495 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34497 var sz = b.el.getSize();
34499 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34507 for (var i = 0; i < this.cols; i++){
34508 mY = Math.max(mY, maxY[i]);
34511 this.el.setHeight(mY - pos.y);
34515 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34517 // var pos = this.el.getBox(true);
34520 // var maxX = pos.right;
34522 // var maxHeight = 0;
34524 // Roo.each(items, function(item, k){
34528 // item.el.position('absolute');
34530 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34532 // item.el.setWidth(width);
34534 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34536 // item.el.setHeight(height);
34539 // item.el.setXY([x, y], isInstant ? false : true);
34541 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34544 // y = y + height + this.alternativePadWidth;
34546 // maxHeight = maxHeight + height + this.alternativePadWidth;
34550 // this.el.setHeight(maxHeight);
34554 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34556 var pos = this.el.getBox(true);
34561 var maxX = pos.right;
34563 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34565 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34567 Roo.each(queue, function(box, k){
34569 Roo.each(box, function(b, kk){
34571 b.el.position('absolute');
34573 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34574 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34576 if(b.size == 'md-left' || b.size == 'md-right'){
34577 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34578 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34581 b.el.setWidth(width);
34582 b.el.setHeight(height);
34590 var positions = [];
34592 switch (box.length){
34594 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34597 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34600 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34603 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34609 Roo.each(box, function(b,kk){
34611 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34613 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34621 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34623 Roo.each(eItems, function(b,k){
34625 b.size = (k == 0) ? 'sm' : 'xs';
34626 b.x = (k == 0) ? 2 : 1;
34627 b.y = (k == 0) ? 2 : 1;
34629 b.el.position('absolute');
34631 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34633 b.el.setWidth(width);
34635 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34637 b.el.setHeight(height);
34641 var positions = [];
34644 x : maxX - this.unitWidth * 2 - this.gutter,
34649 x : maxX - this.unitWidth,
34650 y : minY + (this.unitWidth + this.gutter) * 2
34654 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34658 Roo.each(eItems, function(b,k){
34660 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34666 getVerticalOneBoxColPositions : function(x, y, box)
34670 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34672 if(box[0].size == 'md-left'){
34676 if(box[0].size == 'md-right'){
34681 x : x + (this.unitWidth + this.gutter) * rand,
34688 getVerticalTwoBoxColPositions : function(x, y, box)
34692 if(box[0].size == 'xs'){
34696 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34700 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34714 x : x + (this.unitWidth + this.gutter) * 2,
34715 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34722 getVerticalThreeBoxColPositions : function(x, y, box)
34726 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34734 x : x + (this.unitWidth + this.gutter) * 1,
34739 x : x + (this.unitWidth + this.gutter) * 2,
34747 if(box[0].size == 'xs' && box[1].size == 'xs'){
34756 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34760 x : x + (this.unitWidth + this.gutter) * 1,
34774 x : x + (this.unitWidth + this.gutter) * 2,
34779 x : x + (this.unitWidth + this.gutter) * 2,
34780 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34787 getVerticalFourBoxColPositions : function(x, y, box)
34791 if(box[0].size == 'xs'){
34800 y : y + (this.unitHeight + this.gutter) * 1
34805 y : y + (this.unitHeight + this.gutter) * 2
34809 x : x + (this.unitWidth + this.gutter) * 1,
34823 x : x + (this.unitWidth + this.gutter) * 2,
34828 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34829 y : y + (this.unitHeight + this.gutter) * 1
34833 x : x + (this.unitWidth + this.gutter) * 2,
34834 y : y + (this.unitWidth + this.gutter) * 2
34841 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34845 if(box[0].size == 'md-left'){
34847 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34854 if(box[0].size == 'md-right'){
34856 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34857 y : minY + (this.unitWidth + this.gutter) * 1
34863 var rand = Math.floor(Math.random() * (4 - box[0].y));
34866 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34867 y : minY + (this.unitWidth + this.gutter) * rand
34874 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34878 if(box[0].size == 'xs'){
34881 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34886 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34887 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34895 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34900 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34901 y : minY + (this.unitWidth + this.gutter) * 2
34908 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34912 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34915 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34920 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34921 y : minY + (this.unitWidth + this.gutter) * 1
34925 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34926 y : minY + (this.unitWidth + this.gutter) * 2
34933 if(box[0].size == 'xs' && box[1].size == 'xs'){
34936 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34941 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34946 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34947 y : minY + (this.unitWidth + this.gutter) * 1
34955 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34960 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34961 y : minY + (this.unitWidth + this.gutter) * 2
34965 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34966 y : minY + (this.unitWidth + this.gutter) * 2
34973 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34977 if(box[0].size == 'xs'){
34980 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34985 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34990 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),
34995 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34996 y : minY + (this.unitWidth + this.gutter) * 1
35004 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35009 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35010 y : minY + (this.unitWidth + this.gutter) * 2
35014 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35015 y : minY + (this.unitWidth + this.gutter) * 2
35019 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),
35020 y : minY + (this.unitWidth + this.gutter) * 2
35028 * remove a Masonry Brick
35029 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35031 removeBrick : function(brick_id)
35037 for (var i = 0; i<this.bricks.length; i++) {
35038 if (this.bricks[i].id == brick_id) {
35039 this.bricks.splice(i,1);
35040 this.el.dom.removeChild(Roo.get(brick_id).dom);
35047 * adds a Masonry Brick
35048 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35050 addBrick : function(cfg)
35052 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35053 //this.register(cn);
35054 cn.parentId = this.id;
35055 cn.render(this.el);
35060 * register a Masonry Brick
35061 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35064 register : function(brick)
35066 this.bricks.push(brick);
35067 brick.masonryId = this.id;
35071 * clear all the Masonry Brick
35073 clearAll : function()
35076 //this.getChildContainer().dom.innerHTML = "";
35077 this.el.dom.innerHTML = '';
35080 getSelected : function()
35082 if (!this.selectedBrick) {
35086 return this.selectedBrick;
35090 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35094 * register a Masonry Layout
35095 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35098 register : function(layout)
35100 this.groups[layout.id] = layout;
35103 * fetch a Masonry Layout based on the masonry layout ID
35104 * @param {string} the masonry layout to add
35105 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35108 get: function(layout_id) {
35109 if (typeof(this.groups[layout_id]) == 'undefined') {
35112 return this.groups[layout_id] ;
35124 * http://masonry.desandro.com
35126 * The idea is to render all the bricks based on vertical width...
35128 * The original code extends 'outlayer' - we might need to use that....
35134 * @class Roo.bootstrap.LayoutMasonryAuto
35135 * @extends Roo.bootstrap.Component
35136 * Bootstrap Layout Masonry class
35139 * Create a new Element
35140 * @param {Object} config The config object
35143 Roo.bootstrap.LayoutMasonryAuto = function(config){
35144 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35147 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35150 * @cfg {Boolean} isFitWidth - resize the width..
35152 isFitWidth : false, // options..
35154 * @cfg {Boolean} isOriginLeft = left align?
35156 isOriginLeft : true,
35158 * @cfg {Boolean} isOriginTop = top align?
35160 isOriginTop : false,
35162 * @cfg {Boolean} isLayoutInstant = no animation?
35164 isLayoutInstant : false, // needed?
35166 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35168 isResizingContainer : true,
35170 * @cfg {Number} columnWidth width of the columns
35176 * @cfg {Number} maxCols maximum number of columns
35181 * @cfg {Number} padHeight padding below box..
35187 * @cfg {Boolean} isAutoInitial defalut true
35190 isAutoInitial : true,
35196 initialColumnWidth : 0,
35197 currentSize : null,
35199 colYs : null, // array.
35206 bricks: null, //CompositeElement
35207 cols : 0, // array?
35208 // element : null, // wrapped now this.el
35209 _isLayoutInited : null,
35212 getAutoCreate : function(){
35216 cls: 'blog-masonary-wrapper ' + this.cls,
35218 cls : 'mas-boxes masonary'
35225 getChildContainer: function( )
35227 if (this.boxesEl) {
35228 return this.boxesEl;
35231 this.boxesEl = this.el.select('.mas-boxes').first();
35233 return this.boxesEl;
35237 initEvents : function()
35241 if(this.isAutoInitial){
35242 Roo.log('hook children rendered');
35243 this.on('childrenrendered', function() {
35244 Roo.log('children rendered');
35251 initial : function()
35253 this.reloadItems();
35255 this.currentSize = this.el.getBox(true);
35257 /// was window resize... - let's see if this works..
35258 Roo.EventManager.onWindowResize(this.resize, this);
35260 if(!this.isAutoInitial){
35265 this.layout.defer(500,this);
35268 reloadItems: function()
35270 this.bricks = this.el.select('.masonry-brick', true);
35272 this.bricks.each(function(b) {
35273 //Roo.log(b.getSize());
35274 if (!b.attr('originalwidth')) {
35275 b.attr('originalwidth', b.getSize().width);
35280 Roo.log(this.bricks.elements.length);
35283 resize : function()
35286 var cs = this.el.getBox(true);
35288 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35289 Roo.log("no change in with or X");
35292 this.currentSize = cs;
35296 layout : function()
35299 this._resetLayout();
35300 //this._manageStamps();
35302 // don't animate first layout
35303 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35304 this.layoutItems( isInstant );
35306 // flag for initalized
35307 this._isLayoutInited = true;
35310 layoutItems : function( isInstant )
35312 //var items = this._getItemsForLayout( this.items );
35313 // original code supports filtering layout items.. we just ignore it..
35315 this._layoutItems( this.bricks , isInstant );
35317 this._postLayout();
35319 _layoutItems : function ( items , isInstant)
35321 //this.fireEvent( 'layout', this, items );
35324 if ( !items || !items.elements.length ) {
35325 // no items, emit event with empty array
35330 items.each(function(item) {
35331 Roo.log("layout item");
35333 // get x/y object from method
35334 var position = this._getItemLayoutPosition( item );
35336 position.item = item;
35337 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35338 queue.push( position );
35341 this._processLayoutQueue( queue );
35343 /** Sets position of item in DOM
35344 * @param {Element} item
35345 * @param {Number} x - horizontal position
35346 * @param {Number} y - vertical position
35347 * @param {Boolean} isInstant - disables transitions
35349 _processLayoutQueue : function( queue )
35351 for ( var i=0, len = queue.length; i < len; i++ ) {
35352 var obj = queue[i];
35353 obj.item.position('absolute');
35354 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35360 * Any logic you want to do after each layout,
35361 * i.e. size the container
35363 _postLayout : function()
35365 this.resizeContainer();
35368 resizeContainer : function()
35370 if ( !this.isResizingContainer ) {
35373 var size = this._getContainerSize();
35375 this.el.setSize(size.width,size.height);
35376 this.boxesEl.setSize(size.width,size.height);
35382 _resetLayout : function()
35384 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35385 this.colWidth = this.el.getWidth();
35386 //this.gutter = this.el.getWidth();
35388 this.measureColumns();
35394 this.colYs.push( 0 );
35400 measureColumns : function()
35402 this.getContainerWidth();
35403 // if columnWidth is 0, default to outerWidth of first item
35404 if ( !this.columnWidth ) {
35405 var firstItem = this.bricks.first();
35406 Roo.log(firstItem);
35407 this.columnWidth = this.containerWidth;
35408 if (firstItem && firstItem.attr('originalwidth') ) {
35409 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35411 // columnWidth fall back to item of first element
35412 Roo.log("set column width?");
35413 this.initialColumnWidth = this.columnWidth ;
35415 // if first elem has no width, default to size of container
35420 if (this.initialColumnWidth) {
35421 this.columnWidth = this.initialColumnWidth;
35426 // column width is fixed at the top - however if container width get's smaller we should
35429 // this bit calcs how man columns..
35431 var columnWidth = this.columnWidth += this.gutter;
35433 // calculate columns
35434 var containerWidth = this.containerWidth + this.gutter;
35436 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35437 // fix rounding errors, typically with gutters
35438 var excess = columnWidth - containerWidth % columnWidth;
35441 // if overshoot is less than a pixel, round up, otherwise floor it
35442 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35443 cols = Math[ mathMethod ]( cols );
35444 this.cols = Math.max( cols, 1 );
35445 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35447 // padding positioning..
35448 var totalColWidth = this.cols * this.columnWidth;
35449 var padavail = this.containerWidth - totalColWidth;
35450 // so for 2 columns - we need 3 'pads'
35452 var padNeeded = (1+this.cols) * this.padWidth;
35454 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35456 this.columnWidth += padExtra
35457 //this.padWidth = Math.floor(padavail / ( this.cols));
35459 // adjust colum width so that padding is fixed??
35461 // we have 3 columns ... total = width * 3
35462 // we have X left over... that should be used by
35464 //if (this.expandC) {
35472 getContainerWidth : function()
35474 /* // container is parent if fit width
35475 var container = this.isFitWidth ? this.element.parentNode : this.element;
35476 // check that this.size and size are there
35477 // IE8 triggers resize on body size change, so they might not be
35479 var size = getSize( container ); //FIXME
35480 this.containerWidth = size && size.innerWidth; //FIXME
35483 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35487 _getItemLayoutPosition : function( item ) // what is item?
35489 // we resize the item to our columnWidth..
35491 item.setWidth(this.columnWidth);
35492 item.autoBoxAdjust = false;
35494 var sz = item.getSize();
35496 // how many columns does this brick span
35497 var remainder = this.containerWidth % this.columnWidth;
35499 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35500 // round if off by 1 pixel, otherwise use ceil
35501 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35502 colSpan = Math.min( colSpan, this.cols );
35504 // normally this should be '1' as we dont' currently allow multi width columns..
35506 var colGroup = this._getColGroup( colSpan );
35507 // get the minimum Y value from the columns
35508 var minimumY = Math.min.apply( Math, colGroup );
35509 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35511 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35513 // position the brick
35515 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35516 y: this.currentSize.y + minimumY + this.padHeight
35520 // apply setHeight to necessary columns
35521 var setHeight = minimumY + sz.height + this.padHeight;
35522 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35524 var setSpan = this.cols + 1 - colGroup.length;
35525 for ( var i = 0; i < setSpan; i++ ) {
35526 this.colYs[ shortColIndex + i ] = setHeight ;
35533 * @param {Number} colSpan - number of columns the element spans
35534 * @returns {Array} colGroup
35536 _getColGroup : function( colSpan )
35538 if ( colSpan < 2 ) {
35539 // if brick spans only one column, use all the column Ys
35544 // how many different places could this brick fit horizontally
35545 var groupCount = this.cols + 1 - colSpan;
35546 // for each group potential horizontal position
35547 for ( var i = 0; i < groupCount; i++ ) {
35548 // make an array of colY values for that one group
35549 var groupColYs = this.colYs.slice( i, i + colSpan );
35550 // and get the max value of the array
35551 colGroup[i] = Math.max.apply( Math, groupColYs );
35556 _manageStamp : function( stamp )
35558 var stampSize = stamp.getSize();
35559 var offset = stamp.getBox();
35560 // get the columns that this stamp affects
35561 var firstX = this.isOriginLeft ? offset.x : offset.right;
35562 var lastX = firstX + stampSize.width;
35563 var firstCol = Math.floor( firstX / this.columnWidth );
35564 firstCol = Math.max( 0, firstCol );
35566 var lastCol = Math.floor( lastX / this.columnWidth );
35567 // lastCol should not go over if multiple of columnWidth #425
35568 lastCol -= lastX % this.columnWidth ? 0 : 1;
35569 lastCol = Math.min( this.cols - 1, lastCol );
35571 // set colYs to bottom of the stamp
35572 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35575 for ( var i = firstCol; i <= lastCol; i++ ) {
35576 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35581 _getContainerSize : function()
35583 this.maxY = Math.max.apply( Math, this.colYs );
35588 if ( this.isFitWidth ) {
35589 size.width = this._getContainerFitWidth();
35595 _getContainerFitWidth : function()
35597 var unusedCols = 0;
35598 // count unused columns
35601 if ( this.colYs[i] !== 0 ) {
35606 // fit container to columns that have been used
35607 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35610 needsResizeLayout : function()
35612 var previousWidth = this.containerWidth;
35613 this.getContainerWidth();
35614 return previousWidth !== this.containerWidth;
35629 * @class Roo.bootstrap.MasonryBrick
35630 * @extends Roo.bootstrap.Component
35631 * Bootstrap MasonryBrick class
35634 * Create a new MasonryBrick
35635 * @param {Object} config The config object
35638 Roo.bootstrap.MasonryBrick = function(config){
35640 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35642 Roo.bootstrap.MasonryBrick.register(this);
35648 * When a MasonryBrick is clcik
35649 * @param {Roo.bootstrap.MasonryBrick} this
35650 * @param {Roo.EventObject} e
35656 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35659 * @cfg {String} title
35663 * @cfg {String} html
35667 * @cfg {String} bgimage
35671 * @cfg {String} videourl
35675 * @cfg {String} cls
35679 * @cfg {String} href
35683 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35688 * @cfg {String} placetitle (center|bottom)
35693 * @cfg {Boolean} isFitContainer defalut true
35695 isFitContainer : true,
35698 * @cfg {Boolean} preventDefault defalut false
35700 preventDefault : false,
35703 * @cfg {Boolean} inverse defalut false
35705 maskInverse : false,
35707 getAutoCreate : function()
35709 if(!this.isFitContainer){
35710 return this.getSplitAutoCreate();
35713 var cls = 'masonry-brick masonry-brick-full';
35715 if(this.href.length){
35716 cls += ' masonry-brick-link';
35719 if(this.bgimage.length){
35720 cls += ' masonry-brick-image';
35723 if(this.maskInverse){
35724 cls += ' mask-inverse';
35727 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35728 cls += ' enable-mask';
35732 cls += ' masonry-' + this.size + '-brick';
35735 if(this.placetitle.length){
35737 switch (this.placetitle) {
35739 cls += ' masonry-center-title';
35742 cls += ' masonry-bottom-title';
35749 if(!this.html.length && !this.bgimage.length){
35750 cls += ' masonry-center-title';
35753 if(!this.html.length && this.bgimage.length){
35754 cls += ' masonry-bottom-title';
35759 cls += ' ' + this.cls;
35763 tag: (this.href.length) ? 'a' : 'div',
35768 cls: 'masonry-brick-mask'
35772 cls: 'masonry-brick-paragraph',
35778 if(this.href.length){
35779 cfg.href = this.href;
35782 var cn = cfg.cn[1].cn;
35784 if(this.title.length){
35787 cls: 'masonry-brick-title',
35792 if(this.html.length){
35795 cls: 'masonry-brick-text',
35800 if (!this.title.length && !this.html.length) {
35801 cfg.cn[1].cls += ' hide';
35804 if(this.bgimage.length){
35807 cls: 'masonry-brick-image-view',
35812 if(this.videourl.length){
35813 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35814 // youtube support only?
35817 cls: 'masonry-brick-image-view',
35820 allowfullscreen : true
35828 getSplitAutoCreate : function()
35830 var cls = 'masonry-brick masonry-brick-split';
35832 if(this.href.length){
35833 cls += ' masonry-brick-link';
35836 if(this.bgimage.length){
35837 cls += ' masonry-brick-image';
35841 cls += ' masonry-' + this.size + '-brick';
35844 switch (this.placetitle) {
35846 cls += ' masonry-center-title';
35849 cls += ' masonry-bottom-title';
35852 if(!this.bgimage.length){
35853 cls += ' masonry-center-title';
35856 if(this.bgimage.length){
35857 cls += ' masonry-bottom-title';
35863 cls += ' ' + this.cls;
35867 tag: (this.href.length) ? 'a' : 'div',
35872 cls: 'masonry-brick-split-head',
35876 cls: 'masonry-brick-paragraph',
35883 cls: 'masonry-brick-split-body',
35889 if(this.href.length){
35890 cfg.href = this.href;
35893 if(this.title.length){
35894 cfg.cn[0].cn[0].cn.push({
35896 cls: 'masonry-brick-title',
35901 if(this.html.length){
35902 cfg.cn[1].cn.push({
35904 cls: 'masonry-brick-text',
35909 if(this.bgimage.length){
35910 cfg.cn[0].cn.push({
35912 cls: 'masonry-brick-image-view',
35917 if(this.videourl.length){
35918 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35919 // youtube support only?
35920 cfg.cn[0].cn.cn.push({
35922 cls: 'masonry-brick-image-view',
35925 allowfullscreen : true
35932 initEvents: function()
35934 switch (this.size) {
35967 this.el.on('touchstart', this.onTouchStart, this);
35968 this.el.on('touchmove', this.onTouchMove, this);
35969 this.el.on('touchend', this.onTouchEnd, this);
35970 this.el.on('contextmenu', this.onContextMenu, this);
35972 this.el.on('mouseenter' ,this.enter, this);
35973 this.el.on('mouseleave', this.leave, this);
35974 this.el.on('click', this.onClick, this);
35977 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35978 this.parent().bricks.push(this);
35983 onClick: function(e, el)
35985 var time = this.endTimer - this.startTimer;
35986 // Roo.log(e.preventDefault());
35989 e.preventDefault();
35994 if(!this.preventDefault){
35998 e.preventDefault();
36000 if (this.activeClass != '') {
36001 this.selectBrick();
36004 this.fireEvent('click', this, e);
36007 enter: function(e, el)
36009 e.preventDefault();
36011 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36015 if(this.bgimage.length && this.html.length){
36016 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36020 leave: function(e, el)
36022 e.preventDefault();
36024 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36028 if(this.bgimage.length && this.html.length){
36029 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36033 onTouchStart: function(e, el)
36035 // e.preventDefault();
36037 this.touchmoved = false;
36039 if(!this.isFitContainer){
36043 if(!this.bgimage.length || !this.html.length){
36047 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36049 this.timer = new Date().getTime();
36053 onTouchMove: function(e, el)
36055 this.touchmoved = true;
36058 onContextMenu : function(e,el)
36060 e.preventDefault();
36061 e.stopPropagation();
36065 onTouchEnd: function(e, el)
36067 // e.preventDefault();
36069 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36076 if(!this.bgimage.length || !this.html.length){
36078 if(this.href.length){
36079 window.location.href = this.href;
36085 if(!this.isFitContainer){
36089 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36091 window.location.href = this.href;
36094 //selection on single brick only
36095 selectBrick : function() {
36097 if (!this.parentId) {
36101 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36102 var index = m.selectedBrick.indexOf(this.id);
36105 m.selectedBrick.splice(index,1);
36106 this.el.removeClass(this.activeClass);
36110 for(var i = 0; i < m.selectedBrick.length; i++) {
36111 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36112 b.el.removeClass(b.activeClass);
36115 m.selectedBrick = [];
36117 m.selectedBrick.push(this.id);
36118 this.el.addClass(this.activeClass);
36122 isSelected : function(){
36123 return this.el.hasClass(this.activeClass);
36128 Roo.apply(Roo.bootstrap.MasonryBrick, {
36131 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36133 * register a Masonry Brick
36134 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36137 register : function(brick)
36139 //this.groups[brick.id] = brick;
36140 this.groups.add(brick.id, brick);
36143 * fetch a masonry brick based on the masonry brick ID
36144 * @param {string} the masonry brick to add
36145 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36148 get: function(brick_id)
36150 // if (typeof(this.groups[brick_id]) == 'undefined') {
36153 // return this.groups[brick_id] ;
36155 if(this.groups.key(brick_id)) {
36156 return this.groups.key(brick_id);
36174 * @class Roo.bootstrap.Brick
36175 * @extends Roo.bootstrap.Component
36176 * Bootstrap Brick class
36179 * Create a new Brick
36180 * @param {Object} config The config object
36183 Roo.bootstrap.Brick = function(config){
36184 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36190 * When a Brick is click
36191 * @param {Roo.bootstrap.Brick} this
36192 * @param {Roo.EventObject} e
36198 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36201 * @cfg {String} title
36205 * @cfg {String} html
36209 * @cfg {String} bgimage
36213 * @cfg {String} cls
36217 * @cfg {String} href
36221 * @cfg {String} video
36225 * @cfg {Boolean} square
36229 getAutoCreate : function()
36231 var cls = 'roo-brick';
36233 if(this.href.length){
36234 cls += ' roo-brick-link';
36237 if(this.bgimage.length){
36238 cls += ' roo-brick-image';
36241 if(!this.html.length && !this.bgimage.length){
36242 cls += ' roo-brick-center-title';
36245 if(!this.html.length && this.bgimage.length){
36246 cls += ' roo-brick-bottom-title';
36250 cls += ' ' + this.cls;
36254 tag: (this.href.length) ? 'a' : 'div',
36259 cls: 'roo-brick-paragraph',
36265 if(this.href.length){
36266 cfg.href = this.href;
36269 var cn = cfg.cn[0].cn;
36271 if(this.title.length){
36274 cls: 'roo-brick-title',
36279 if(this.html.length){
36282 cls: 'roo-brick-text',
36289 if(this.bgimage.length){
36292 cls: 'roo-brick-image-view',
36300 initEvents: function()
36302 if(this.title.length || this.html.length){
36303 this.el.on('mouseenter' ,this.enter, this);
36304 this.el.on('mouseleave', this.leave, this);
36307 Roo.EventManager.onWindowResize(this.resize, this);
36309 if(this.bgimage.length){
36310 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36311 this.imageEl.on('load', this.onImageLoad, this);
36318 onImageLoad : function()
36323 resize : function()
36325 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36327 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36329 if(this.bgimage.length){
36330 var image = this.el.select('.roo-brick-image-view', true).first();
36332 image.setWidth(paragraph.getWidth());
36335 image.setHeight(paragraph.getWidth());
36338 this.el.setHeight(image.getHeight());
36339 paragraph.setHeight(image.getHeight());
36345 enter: function(e, el)
36347 e.preventDefault();
36349 if(this.bgimage.length){
36350 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36351 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36355 leave: function(e, el)
36357 e.preventDefault();
36359 if(this.bgimage.length){
36360 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36361 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36376 * @class Roo.bootstrap.NumberField
36377 * @extends Roo.bootstrap.Input
36378 * Bootstrap NumberField class
36384 * Create a new NumberField
36385 * @param {Object} config The config object
36388 Roo.bootstrap.NumberField = function(config){
36389 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36392 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36395 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36397 allowDecimals : true,
36399 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36401 decimalSeparator : ".",
36403 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36405 decimalPrecision : 2,
36407 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36409 allowNegative : true,
36412 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36416 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36418 minValue : Number.NEGATIVE_INFINITY,
36420 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36422 maxValue : Number.MAX_VALUE,
36424 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36426 minText : "The minimum value for this field is {0}",
36428 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36430 maxText : "The maximum value for this field is {0}",
36432 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36433 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36435 nanText : "{0} is not a valid number",
36437 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36439 thousandsDelimiter : false,
36441 * @cfg {String} valueAlign alignment of value
36443 valueAlign : "left",
36445 getAutoCreate : function()
36447 var hiddenInput = {
36451 cls: 'hidden-number-input'
36455 hiddenInput.name = this.name;
36460 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36462 this.name = hiddenInput.name;
36464 if(cfg.cn.length > 0) {
36465 cfg.cn.push(hiddenInput);
36472 initEvents : function()
36474 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36476 var allowed = "0123456789";
36478 if(this.allowDecimals){
36479 allowed += this.decimalSeparator;
36482 if(this.allowNegative){
36486 if(this.thousandsDelimiter) {
36490 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36492 var keyPress = function(e){
36494 var k = e.getKey();
36496 var c = e.getCharCode();
36499 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36500 allowed.indexOf(String.fromCharCode(c)) === -1
36506 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36510 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36515 this.el.on("keypress", keyPress, this);
36518 validateValue : function(value)
36521 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36525 var num = this.parseValue(value);
36528 this.markInvalid(String.format(this.nanText, value));
36532 if(num < this.minValue){
36533 this.markInvalid(String.format(this.minText, this.minValue));
36537 if(num > this.maxValue){
36538 this.markInvalid(String.format(this.maxText, this.maxValue));
36545 getValue : function()
36547 var v = this.hiddenEl().getValue();
36549 return this.fixPrecision(this.parseValue(v));
36552 parseValue : function(value)
36554 if(this.thousandsDelimiter) {
36556 r = new RegExp(",", "g");
36557 value = value.replace(r, "");
36560 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36561 return isNaN(value) ? '' : value;
36564 fixPrecision : function(value)
36566 if(this.thousandsDelimiter) {
36568 r = new RegExp(",", "g");
36569 value = value.replace(r, "");
36572 var nan = isNaN(value);
36574 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36575 return nan ? '' : value;
36577 return parseFloat(value).toFixed(this.decimalPrecision);
36580 setValue : function(v)
36582 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36588 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36590 this.inputEl().dom.value = (v == '') ? '' :
36591 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36593 if(!this.allowZero && v === '0') {
36594 this.hiddenEl().dom.value = '';
36595 this.inputEl().dom.value = '';
36602 decimalPrecisionFcn : function(v)
36604 return Math.floor(v);
36607 beforeBlur : function()
36609 var v = this.parseValue(this.getRawValue());
36611 if(v || v === 0 || v === ''){
36616 hiddenEl : function()
36618 return this.el.select('input.hidden-number-input',true).first();
36630 * @class Roo.bootstrap.DocumentSlider
36631 * @extends Roo.bootstrap.Component
36632 * Bootstrap DocumentSlider class
36635 * Create a new DocumentViewer
36636 * @param {Object} config The config object
36639 Roo.bootstrap.DocumentSlider = function(config){
36640 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36647 * Fire after initEvent
36648 * @param {Roo.bootstrap.DocumentSlider} this
36653 * Fire after update
36654 * @param {Roo.bootstrap.DocumentSlider} this
36660 * @param {Roo.bootstrap.DocumentSlider} this
36666 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36672 getAutoCreate : function()
36676 cls : 'roo-document-slider',
36680 cls : 'roo-document-slider-header',
36684 cls : 'roo-document-slider-header-title'
36690 cls : 'roo-document-slider-body',
36694 cls : 'roo-document-slider-prev',
36698 cls : 'fa fa-chevron-left'
36704 cls : 'roo-document-slider-thumb',
36708 cls : 'roo-document-slider-image'
36714 cls : 'roo-document-slider-next',
36718 cls : 'fa fa-chevron-right'
36730 initEvents : function()
36732 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36733 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36735 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36736 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36738 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36739 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36741 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36742 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36744 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36745 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36747 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36748 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36750 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36751 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36753 this.thumbEl.on('click', this.onClick, this);
36755 this.prevIndicator.on('click', this.prev, this);
36757 this.nextIndicator.on('click', this.next, this);
36761 initial : function()
36763 if(this.files.length){
36764 this.indicator = 1;
36768 this.fireEvent('initial', this);
36771 update : function()
36773 this.imageEl.attr('src', this.files[this.indicator - 1]);
36775 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36777 this.prevIndicator.show();
36779 if(this.indicator == 1){
36780 this.prevIndicator.hide();
36783 this.nextIndicator.show();
36785 if(this.indicator == this.files.length){
36786 this.nextIndicator.hide();
36789 this.thumbEl.scrollTo('top');
36791 this.fireEvent('update', this);
36794 onClick : function(e)
36796 e.preventDefault();
36798 this.fireEvent('click', this);
36803 e.preventDefault();
36805 this.indicator = Math.max(1, this.indicator - 1);
36812 e.preventDefault();
36814 this.indicator = Math.min(this.files.length, this.indicator + 1);
36828 * @class Roo.bootstrap.RadioSet
36829 * @extends Roo.bootstrap.Input
36830 * Bootstrap RadioSet class
36831 * @cfg {String} indicatorpos (left|right) default left
36832 * @cfg {Boolean} inline (true|false) inline the element (default true)
36833 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36835 * Create a new RadioSet
36836 * @param {Object} config The config object
36839 Roo.bootstrap.RadioSet = function(config){
36841 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36845 Roo.bootstrap.RadioSet.register(this);
36850 * Fires when the element is checked or unchecked.
36851 * @param {Roo.bootstrap.RadioSet} this This radio
36852 * @param {Roo.bootstrap.Radio} item The checked item
36857 * Fires when the element is click.
36858 * @param {Roo.bootstrap.RadioSet} this This radio set
36859 * @param {Roo.bootstrap.Radio} item The checked item
36860 * @param {Roo.EventObject} e The event object
36867 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36875 indicatorpos : 'left',
36877 getAutoCreate : function()
36881 cls : 'roo-radio-set-label',
36885 html : this.fieldLabel
36889 if (Roo.bootstrap.version == 3) {
36892 if(this.indicatorpos == 'left'){
36895 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36896 tooltip : 'This field is required'
36901 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36902 tooltip : 'This field is required'
36908 cls : 'roo-radio-set-items'
36911 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36913 if (align === 'left' && this.fieldLabel.length) {
36916 cls : "roo-radio-set-right",
36922 if(this.labelWidth > 12){
36923 label.style = "width: " + this.labelWidth + 'px';
36926 if(this.labelWidth < 13 && this.labelmd == 0){
36927 this.labelmd = this.labelWidth;
36930 if(this.labellg > 0){
36931 label.cls += ' col-lg-' + this.labellg;
36932 items.cls += ' col-lg-' + (12 - this.labellg);
36935 if(this.labelmd > 0){
36936 label.cls += ' col-md-' + this.labelmd;
36937 items.cls += ' col-md-' + (12 - this.labelmd);
36940 if(this.labelsm > 0){
36941 label.cls += ' col-sm-' + this.labelsm;
36942 items.cls += ' col-sm-' + (12 - this.labelsm);
36945 if(this.labelxs > 0){
36946 label.cls += ' col-xs-' + this.labelxs;
36947 items.cls += ' col-xs-' + (12 - this.labelxs);
36953 cls : 'roo-radio-set',
36957 cls : 'roo-radio-set-input',
36960 value : this.value ? this.value : ''
36967 if(this.weight.length){
36968 cfg.cls += ' roo-radio-' + this.weight;
36972 cfg.cls += ' roo-radio-set-inline';
36976 ['xs','sm','md','lg'].map(function(size){
36977 if (settings[size]) {
36978 cfg.cls += ' col-' + size + '-' + settings[size];
36986 initEvents : function()
36988 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36989 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36991 if(!this.fieldLabel.length){
36992 this.labelEl.hide();
36995 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36996 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36998 this.indicator = this.indicatorEl();
37000 if(this.indicator){
37001 this.indicator.addClass('invisible');
37004 this.originalValue = this.getValue();
37008 inputEl: function ()
37010 return this.el.select('.roo-radio-set-input', true).first();
37013 getChildContainer : function()
37015 return this.itemsEl;
37018 register : function(item)
37020 this.radioes.push(item);
37024 validate : function()
37026 if(this.getVisibilityEl().hasClass('hidden')){
37032 Roo.each(this.radioes, function(i){
37041 if(this.allowBlank) {
37045 if(this.disabled || valid){
37050 this.markInvalid();
37055 markValid : function()
37057 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37058 this.indicatorEl().removeClass('visible');
37059 this.indicatorEl().addClass('invisible');
37063 if (Roo.bootstrap.version == 3) {
37064 this.el.removeClass([this.invalidClass, this.validClass]);
37065 this.el.addClass(this.validClass);
37067 this.el.removeClass(['is-invalid','is-valid']);
37068 this.el.addClass(['is-valid']);
37070 this.fireEvent('valid', this);
37073 markInvalid : function(msg)
37075 if(this.allowBlank || this.disabled){
37079 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37080 this.indicatorEl().removeClass('invisible');
37081 this.indicatorEl().addClass('visible');
37083 if (Roo.bootstrap.version == 3) {
37084 this.el.removeClass([this.invalidClass, this.validClass]);
37085 this.el.addClass(this.invalidClass);
37087 this.el.removeClass(['is-invalid','is-valid']);
37088 this.el.addClass(['is-invalid']);
37091 this.fireEvent('invalid', this, msg);
37095 setValue : function(v, suppressEvent)
37097 if(this.value === v){
37104 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37107 Roo.each(this.radioes, function(i){
37109 i.el.removeClass('checked');
37112 Roo.each(this.radioes, function(i){
37114 if(i.value === v || i.value.toString() === v.toString()){
37116 i.el.addClass('checked');
37118 if(suppressEvent !== true){
37119 this.fireEvent('check', this, i);
37130 clearInvalid : function(){
37132 if(!this.el || this.preventMark){
37136 this.el.removeClass([this.invalidClass]);
37138 this.fireEvent('valid', this);
37143 Roo.apply(Roo.bootstrap.RadioSet, {
37147 register : function(set)
37149 this.groups[set.name] = set;
37152 get: function(name)
37154 if (typeof(this.groups[name]) == 'undefined') {
37158 return this.groups[name] ;
37164 * Ext JS Library 1.1.1
37165 * Copyright(c) 2006-2007, Ext JS, LLC.
37167 * Originally Released Under LGPL - original licence link has changed is not relivant.
37170 * <script type="text/javascript">
37175 * @class Roo.bootstrap.SplitBar
37176 * @extends Roo.util.Observable
37177 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37181 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37182 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37183 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37184 split.minSize = 100;
37185 split.maxSize = 600;
37186 split.animate = true;
37187 split.on('moved', splitterMoved);
37190 * Create a new SplitBar
37191 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37192 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37193 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37194 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37195 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37196 position of the SplitBar).
37198 Roo.bootstrap.SplitBar = function(cfg){
37203 // dragElement : elm
37204 // resizingElement: el,
37206 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37207 // placement : Roo.bootstrap.SplitBar.LEFT ,
37208 // existingProxy ???
37211 this.el = Roo.get(cfg.dragElement, true);
37212 this.el.dom.unselectable = "on";
37214 this.resizingEl = Roo.get(cfg.resizingElement, true);
37218 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37219 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37222 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37225 * The minimum size of the resizing element. (Defaults to 0)
37231 * The maximum size of the resizing element. (Defaults to 2000)
37234 this.maxSize = 2000;
37237 * Whether to animate the transition to the new size
37240 this.animate = false;
37243 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37246 this.useShim = false;
37251 if(!cfg.existingProxy){
37253 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37255 this.proxy = Roo.get(cfg.existingProxy).dom;
37258 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37261 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37264 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37267 this.dragSpecs = {};
37270 * @private The adapter to use to positon and resize elements
37272 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37273 this.adapter.init(this);
37275 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37277 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37278 this.el.addClass("roo-splitbar-h");
37281 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37282 this.el.addClass("roo-splitbar-v");
37288 * Fires when the splitter is moved (alias for {@link #event-moved})
37289 * @param {Roo.bootstrap.SplitBar} this
37290 * @param {Number} newSize the new width or height
37295 * Fires when the splitter is moved
37296 * @param {Roo.bootstrap.SplitBar} this
37297 * @param {Number} newSize the new width or height
37301 * @event beforeresize
37302 * Fires before the splitter is dragged
37303 * @param {Roo.bootstrap.SplitBar} this
37305 "beforeresize" : true,
37307 "beforeapply" : true
37310 Roo.util.Observable.call(this);
37313 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37314 onStartProxyDrag : function(x, y){
37315 this.fireEvent("beforeresize", this);
37317 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37319 o.enableDisplayMode("block");
37320 // all splitbars share the same overlay
37321 Roo.bootstrap.SplitBar.prototype.overlay = o;
37323 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37324 this.overlay.show();
37325 Roo.get(this.proxy).setDisplayed("block");
37326 var size = this.adapter.getElementSize(this);
37327 this.activeMinSize = this.getMinimumSize();;
37328 this.activeMaxSize = this.getMaximumSize();;
37329 var c1 = size - this.activeMinSize;
37330 var c2 = Math.max(this.activeMaxSize - size, 0);
37331 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37332 this.dd.resetConstraints();
37333 this.dd.setXConstraint(
37334 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37335 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37337 this.dd.setYConstraint(0, 0);
37339 this.dd.resetConstraints();
37340 this.dd.setXConstraint(0, 0);
37341 this.dd.setYConstraint(
37342 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37343 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37346 this.dragSpecs.startSize = size;
37347 this.dragSpecs.startPoint = [x, y];
37348 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37352 * @private Called after the drag operation by the DDProxy
37354 onEndProxyDrag : function(e){
37355 Roo.get(this.proxy).setDisplayed(false);
37356 var endPoint = Roo.lib.Event.getXY(e);
37358 this.overlay.hide();
37361 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37362 newSize = this.dragSpecs.startSize +
37363 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37364 endPoint[0] - this.dragSpecs.startPoint[0] :
37365 this.dragSpecs.startPoint[0] - endPoint[0]
37368 newSize = this.dragSpecs.startSize +
37369 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37370 endPoint[1] - this.dragSpecs.startPoint[1] :
37371 this.dragSpecs.startPoint[1] - endPoint[1]
37374 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37375 if(newSize != this.dragSpecs.startSize){
37376 if(this.fireEvent('beforeapply', this, newSize) !== false){
37377 this.adapter.setElementSize(this, newSize);
37378 this.fireEvent("moved", this, newSize);
37379 this.fireEvent("resize", this, newSize);
37385 * Get the adapter this SplitBar uses
37386 * @return The adapter object
37388 getAdapter : function(){
37389 return this.adapter;
37393 * Set the adapter this SplitBar uses
37394 * @param {Object} adapter A SplitBar adapter object
37396 setAdapter : function(adapter){
37397 this.adapter = adapter;
37398 this.adapter.init(this);
37402 * Gets the minimum size for the resizing element
37403 * @return {Number} The minimum size
37405 getMinimumSize : function(){
37406 return this.minSize;
37410 * Sets the minimum size for the resizing element
37411 * @param {Number} minSize The minimum size
37413 setMinimumSize : function(minSize){
37414 this.minSize = minSize;
37418 * Gets the maximum size for the resizing element
37419 * @return {Number} The maximum size
37421 getMaximumSize : function(){
37422 return this.maxSize;
37426 * Sets the maximum size for the resizing element
37427 * @param {Number} maxSize The maximum size
37429 setMaximumSize : function(maxSize){
37430 this.maxSize = maxSize;
37434 * Sets the initialize size for the resizing element
37435 * @param {Number} size The initial size
37437 setCurrentSize : function(size){
37438 var oldAnimate = this.animate;
37439 this.animate = false;
37440 this.adapter.setElementSize(this, size);
37441 this.animate = oldAnimate;
37445 * Destroy this splitbar.
37446 * @param {Boolean} removeEl True to remove the element
37448 destroy : function(removeEl){
37450 this.shim.remove();
37453 this.proxy.parentNode.removeChild(this.proxy);
37461 * @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.
37463 Roo.bootstrap.SplitBar.createProxy = function(dir){
37464 var proxy = new Roo.Element(document.createElement("div"));
37465 proxy.unselectable();
37466 var cls = 'roo-splitbar-proxy';
37467 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37468 document.body.appendChild(proxy.dom);
37473 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37474 * Default Adapter. It assumes the splitter and resizing element are not positioned
37475 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37477 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37480 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37481 // do nothing for now
37482 init : function(s){
37486 * Called before drag operations to get the current size of the resizing element.
37487 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37489 getElementSize : function(s){
37490 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37491 return s.resizingEl.getWidth();
37493 return s.resizingEl.getHeight();
37498 * Called after drag operations to set the size of the resizing element.
37499 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37500 * @param {Number} newSize The new size to set
37501 * @param {Function} onComplete A function to be invoked when resizing is complete
37503 setElementSize : function(s, newSize, onComplete){
37504 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37506 s.resizingEl.setWidth(newSize);
37508 onComplete(s, newSize);
37511 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37516 s.resizingEl.setHeight(newSize);
37518 onComplete(s, newSize);
37521 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37528 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37529 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37530 * Adapter that moves the splitter element to align with the resized sizing element.
37531 * Used with an absolute positioned SplitBar.
37532 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37533 * document.body, make sure you assign an id to the body element.
37535 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37536 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37537 this.container = Roo.get(container);
37540 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37541 init : function(s){
37542 this.basic.init(s);
37545 getElementSize : function(s){
37546 return this.basic.getElementSize(s);
37549 setElementSize : function(s, newSize, onComplete){
37550 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37553 moveSplitter : function(s){
37554 var yes = Roo.bootstrap.SplitBar;
37555 switch(s.placement){
37557 s.el.setX(s.resizingEl.getRight());
37560 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37563 s.el.setY(s.resizingEl.getBottom());
37566 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37573 * Orientation constant - Create a vertical SplitBar
37577 Roo.bootstrap.SplitBar.VERTICAL = 1;
37580 * Orientation constant - Create a horizontal SplitBar
37584 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37587 * Placement constant - The resizing element is to the left of the splitter element
37591 Roo.bootstrap.SplitBar.LEFT = 1;
37594 * Placement constant - The resizing element is to the right of the splitter element
37598 Roo.bootstrap.SplitBar.RIGHT = 2;
37601 * Placement constant - The resizing element is positioned above the splitter element
37605 Roo.bootstrap.SplitBar.TOP = 3;
37608 * Placement constant - The resizing element is positioned under splitter element
37612 Roo.bootstrap.SplitBar.BOTTOM = 4;
37613 Roo.namespace("Roo.bootstrap.layout");/*
37615 * Ext JS Library 1.1.1
37616 * Copyright(c) 2006-2007, Ext JS, LLC.
37618 * Originally Released Under LGPL - original licence link has changed is not relivant.
37621 * <script type="text/javascript">
37625 * @class Roo.bootstrap.layout.Manager
37626 * @extends Roo.bootstrap.Component
37627 * Base class for layout managers.
37629 Roo.bootstrap.layout.Manager = function(config)
37631 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37637 /** false to disable window resize monitoring @type Boolean */
37638 this.monitorWindowResize = true;
37643 * Fires when a layout is performed.
37644 * @param {Roo.LayoutManager} this
37648 * @event regionresized
37649 * Fires when the user resizes a region.
37650 * @param {Roo.LayoutRegion} region The resized region
37651 * @param {Number} newSize The new size (width for east/west, height for north/south)
37653 "regionresized" : true,
37655 * @event regioncollapsed
37656 * Fires when a region is collapsed.
37657 * @param {Roo.LayoutRegion} region The collapsed region
37659 "regioncollapsed" : true,
37661 * @event regionexpanded
37662 * Fires when a region is expanded.
37663 * @param {Roo.LayoutRegion} region The expanded region
37665 "regionexpanded" : true
37667 this.updating = false;
37670 this.el = Roo.get(config.el);
37676 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37681 monitorWindowResize : true,
37687 onRender : function(ct, position)
37690 this.el = Roo.get(ct);
37693 //this.fireEvent('render',this);
37697 initEvents: function()
37701 // ie scrollbar fix
37702 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37703 document.body.scroll = "no";
37704 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37705 this.el.position('relative');
37707 this.id = this.el.id;
37708 this.el.addClass("roo-layout-container");
37709 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37710 if(this.el.dom != document.body ) {
37711 this.el.on('resize', this.layout,this);
37712 this.el.on('show', this.layout,this);
37718 * Returns true if this layout is currently being updated
37719 * @return {Boolean}
37721 isUpdating : function(){
37722 return this.updating;
37726 * Suspend the LayoutManager from doing auto-layouts while
37727 * making multiple add or remove calls
37729 beginUpdate : function(){
37730 this.updating = true;
37734 * Restore auto-layouts and optionally disable the manager from performing a layout
37735 * @param {Boolean} noLayout true to disable a layout update
37737 endUpdate : function(noLayout){
37738 this.updating = false;
37744 layout: function(){
37748 onRegionResized : function(region, newSize){
37749 this.fireEvent("regionresized", region, newSize);
37753 onRegionCollapsed : function(region){
37754 this.fireEvent("regioncollapsed", region);
37757 onRegionExpanded : function(region){
37758 this.fireEvent("regionexpanded", region);
37762 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37763 * performs box-model adjustments.
37764 * @return {Object} The size as an object {width: (the width), height: (the height)}
37766 getViewSize : function()
37769 if(this.el.dom != document.body){
37770 size = this.el.getSize();
37772 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37774 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37775 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37780 * Returns the Element this layout is bound to.
37781 * @return {Roo.Element}
37783 getEl : function(){
37788 * Returns the specified region.
37789 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37790 * @return {Roo.LayoutRegion}
37792 getRegion : function(target){
37793 return this.regions[target.toLowerCase()];
37796 onWindowResize : function(){
37797 if(this.monitorWindowResize){
37804 * Ext JS Library 1.1.1
37805 * Copyright(c) 2006-2007, Ext JS, LLC.
37807 * Originally Released Under LGPL - original licence link has changed is not relivant.
37810 * <script type="text/javascript">
37813 * @class Roo.bootstrap.layout.Border
37814 * @extends Roo.bootstrap.layout.Manager
37815 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37816 * please see: examples/bootstrap/nested.html<br><br>
37818 <b>The container the layout is rendered into can be either the body element or any other element.
37819 If it is not the body element, the container needs to either be an absolute positioned element,
37820 or you will need to add "position:relative" to the css of the container. You will also need to specify
37821 the container size if it is not the body element.</b>
37824 * Create a new Border
37825 * @param {Object} config Configuration options
37827 Roo.bootstrap.layout.Border = function(config){
37828 config = config || {};
37829 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37833 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37834 if(config[region]){
37835 config[region].region = region;
37836 this.addRegion(config[region]);
37842 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37844 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37846 parent : false, // this might point to a 'nest' or a ???
37849 * Creates and adds a new region if it doesn't already exist.
37850 * @param {String} target The target region key (north, south, east, west or center).
37851 * @param {Object} config The regions config object
37852 * @return {BorderLayoutRegion} The new region
37854 addRegion : function(config)
37856 if(!this.regions[config.region]){
37857 var r = this.factory(config);
37858 this.bindRegion(r);
37860 return this.regions[config.region];
37864 bindRegion : function(r){
37865 this.regions[r.config.region] = r;
37867 r.on("visibilitychange", this.layout, this);
37868 r.on("paneladded", this.layout, this);
37869 r.on("panelremoved", this.layout, this);
37870 r.on("invalidated", this.layout, this);
37871 r.on("resized", this.onRegionResized, this);
37872 r.on("collapsed", this.onRegionCollapsed, this);
37873 r.on("expanded", this.onRegionExpanded, this);
37877 * Performs a layout update.
37879 layout : function()
37881 if(this.updating) {
37885 // render all the rebions if they have not been done alreayd?
37886 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37887 if(this.regions[region] && !this.regions[region].bodyEl){
37888 this.regions[region].onRender(this.el)
37892 var size = this.getViewSize();
37893 var w = size.width;
37894 var h = size.height;
37899 //var x = 0, y = 0;
37901 var rs = this.regions;
37902 var north = rs["north"];
37903 var south = rs["south"];
37904 var west = rs["west"];
37905 var east = rs["east"];
37906 var center = rs["center"];
37907 //if(this.hideOnLayout){ // not supported anymore
37908 //c.el.setStyle("display", "none");
37910 if(north && north.isVisible()){
37911 var b = north.getBox();
37912 var m = north.getMargins();
37913 b.width = w - (m.left+m.right);
37916 centerY = b.height + b.y + m.bottom;
37917 centerH -= centerY;
37918 north.updateBox(this.safeBox(b));
37920 if(south && south.isVisible()){
37921 var b = south.getBox();
37922 var m = south.getMargins();
37923 b.width = w - (m.left+m.right);
37925 var totalHeight = (b.height + m.top + m.bottom);
37926 b.y = h - totalHeight + m.top;
37927 centerH -= totalHeight;
37928 south.updateBox(this.safeBox(b));
37930 if(west && west.isVisible()){
37931 var b = west.getBox();
37932 var m = west.getMargins();
37933 b.height = centerH - (m.top+m.bottom);
37935 b.y = centerY + m.top;
37936 var totalWidth = (b.width + m.left + m.right);
37937 centerX += totalWidth;
37938 centerW -= totalWidth;
37939 west.updateBox(this.safeBox(b));
37941 if(east && east.isVisible()){
37942 var b = east.getBox();
37943 var m = east.getMargins();
37944 b.height = centerH - (m.top+m.bottom);
37945 var totalWidth = (b.width + m.left + m.right);
37946 b.x = w - totalWidth + m.left;
37947 b.y = centerY + m.top;
37948 centerW -= totalWidth;
37949 east.updateBox(this.safeBox(b));
37952 var m = center.getMargins();
37954 x: centerX + m.left,
37955 y: centerY + m.top,
37956 width: centerW - (m.left+m.right),
37957 height: centerH - (m.top+m.bottom)
37959 //if(this.hideOnLayout){
37960 //center.el.setStyle("display", "block");
37962 center.updateBox(this.safeBox(centerBox));
37965 this.fireEvent("layout", this);
37969 safeBox : function(box){
37970 box.width = Math.max(0, box.width);
37971 box.height = Math.max(0, box.height);
37976 * Adds a ContentPanel (or subclass) to this layout.
37977 * @param {String} target The target region key (north, south, east, west or center).
37978 * @param {Roo.ContentPanel} panel The panel to add
37979 * @return {Roo.ContentPanel} The added panel
37981 add : function(target, panel){
37983 target = target.toLowerCase();
37984 return this.regions[target].add(panel);
37988 * Remove a ContentPanel (or subclass) to this layout.
37989 * @param {String} target The target region key (north, south, east, west or center).
37990 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37991 * @return {Roo.ContentPanel} The removed panel
37993 remove : function(target, panel){
37994 target = target.toLowerCase();
37995 return this.regions[target].remove(panel);
37999 * Searches all regions for a panel with the specified id
38000 * @param {String} panelId
38001 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38003 findPanel : function(panelId){
38004 var rs = this.regions;
38005 for(var target in rs){
38006 if(typeof rs[target] != "function"){
38007 var p = rs[target].getPanel(panelId);
38017 * Searches all regions for a panel with the specified id and activates (shows) it.
38018 * @param {String/ContentPanel} panelId The panels id or the panel itself
38019 * @return {Roo.ContentPanel} The shown panel or null
38021 showPanel : function(panelId) {
38022 var rs = this.regions;
38023 for(var target in rs){
38024 var r = rs[target];
38025 if(typeof r != "function"){
38026 if(r.hasPanel(panelId)){
38027 return r.showPanel(panelId);
38035 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38036 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38039 restoreState : function(provider){
38041 provider = Roo.state.Manager;
38043 var sm = new Roo.LayoutStateManager();
38044 sm.init(this, provider);
38050 * Adds a xtype elements to the layout.
38054 xtype : 'ContentPanel',
38061 xtype : 'NestedLayoutPanel',
38067 items : [ ... list of content panels or nested layout panels.. ]
38071 * @param {Object} cfg Xtype definition of item to add.
38073 addxtype : function(cfg)
38075 // basically accepts a pannel...
38076 // can accept a layout region..!?!?
38077 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38080 // theory? children can only be panels??
38082 //if (!cfg.xtype.match(/Panel$/)) {
38087 if (typeof(cfg.region) == 'undefined') {
38088 Roo.log("Failed to add Panel, region was not set");
38092 var region = cfg.region;
38098 xitems = cfg.items;
38103 if ( region == 'center') {
38104 Roo.log("Center: " + cfg.title);
38110 case 'Content': // ContentPanel (el, cfg)
38111 case 'Scroll': // ContentPanel (el, cfg)
38113 cfg.autoCreate = cfg.autoCreate || true;
38114 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38116 // var el = this.el.createChild();
38117 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38120 this.add(region, ret);
38124 case 'TreePanel': // our new panel!
38125 cfg.el = this.el.createChild();
38126 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38127 this.add(region, ret);
38132 // create a new Layout (which is a Border Layout...
38134 var clayout = cfg.layout;
38135 clayout.el = this.el.createChild();
38136 clayout.items = clayout.items || [];
38140 // replace this exitems with the clayout ones..
38141 xitems = clayout.items;
38143 // force background off if it's in center...
38144 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38145 cfg.background = false;
38147 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38150 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38151 //console.log('adding nested layout panel ' + cfg.toSource());
38152 this.add(region, ret);
38153 nb = {}; /// find first...
38158 // needs grid and region
38160 //var el = this.getRegion(region).el.createChild();
38162 *var el = this.el.createChild();
38163 // create the grid first...
38164 cfg.grid.container = el;
38165 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38168 if (region == 'center' && this.active ) {
38169 cfg.background = false;
38172 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38174 this.add(region, ret);
38176 if (cfg.background) {
38177 // render grid on panel activation (if panel background)
38178 ret.on('activate', function(gp) {
38179 if (!gp.grid.rendered) {
38180 // gp.grid.render(el);
38184 // cfg.grid.render(el);
38190 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38191 // it was the old xcomponent building that caused this before.
38192 // espeically if border is the top element in the tree.
38202 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38204 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38205 this.add(region, ret);
38209 throw "Can not add '" + cfg.xtype + "' to Border";
38215 this.beginUpdate();
38219 Roo.each(xitems, function(i) {
38220 region = nb && i.region ? i.region : false;
38222 var add = ret.addxtype(i);
38225 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38226 if (!i.background) {
38227 abn[region] = nb[region] ;
38234 // make the last non-background panel active..
38235 //if (nb) { Roo.log(abn); }
38238 for(var r in abn) {
38239 region = this.getRegion(r);
38241 // tried using nb[r], but it does not work..
38243 region.showPanel(abn[r]);
38254 factory : function(cfg)
38257 var validRegions = Roo.bootstrap.layout.Border.regions;
38259 var target = cfg.region;
38262 var r = Roo.bootstrap.layout;
38266 return new r.North(cfg);
38268 return new r.South(cfg);
38270 return new r.East(cfg);
38272 return new r.West(cfg);
38274 return new r.Center(cfg);
38276 throw 'Layout region "'+target+'" not supported.';
38283 * Ext JS Library 1.1.1
38284 * Copyright(c) 2006-2007, Ext JS, LLC.
38286 * Originally Released Under LGPL - original licence link has changed is not relivant.
38289 * <script type="text/javascript">
38293 * @class Roo.bootstrap.layout.Basic
38294 * @extends Roo.util.Observable
38295 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38296 * and does not have a titlebar, tabs or any other features. All it does is size and position
38297 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38298 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38299 * @cfg {string} region the region that it inhabits..
38300 * @cfg {bool} skipConfig skip config?
38304 Roo.bootstrap.layout.Basic = function(config){
38306 this.mgr = config.mgr;
38308 this.position = config.region;
38310 var skipConfig = config.skipConfig;
38314 * @scope Roo.BasicLayoutRegion
38318 * @event beforeremove
38319 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38320 * @param {Roo.LayoutRegion} this
38321 * @param {Roo.ContentPanel} panel The panel
38322 * @param {Object} e The cancel event object
38324 "beforeremove" : true,
38326 * @event invalidated
38327 * Fires when the layout for this region is changed.
38328 * @param {Roo.LayoutRegion} this
38330 "invalidated" : true,
38332 * @event visibilitychange
38333 * Fires when this region is shown or hidden
38334 * @param {Roo.LayoutRegion} this
38335 * @param {Boolean} visibility true or false
38337 "visibilitychange" : true,
38339 * @event paneladded
38340 * Fires when a panel is added.
38341 * @param {Roo.LayoutRegion} this
38342 * @param {Roo.ContentPanel} panel The panel
38344 "paneladded" : true,
38346 * @event panelremoved
38347 * Fires when a panel is removed.
38348 * @param {Roo.LayoutRegion} this
38349 * @param {Roo.ContentPanel} panel The panel
38351 "panelremoved" : true,
38353 * @event beforecollapse
38354 * Fires when this region before collapse.
38355 * @param {Roo.LayoutRegion} this
38357 "beforecollapse" : true,
38360 * Fires when this region is collapsed.
38361 * @param {Roo.LayoutRegion} this
38363 "collapsed" : true,
38366 * Fires when this region is expanded.
38367 * @param {Roo.LayoutRegion} this
38372 * Fires when this region is slid into view.
38373 * @param {Roo.LayoutRegion} this
38375 "slideshow" : true,
38378 * Fires when this region slides out of view.
38379 * @param {Roo.LayoutRegion} this
38381 "slidehide" : true,
38383 * @event panelactivated
38384 * Fires when a panel is activated.
38385 * @param {Roo.LayoutRegion} this
38386 * @param {Roo.ContentPanel} panel The activated panel
38388 "panelactivated" : true,
38391 * Fires when the user resizes this region.
38392 * @param {Roo.LayoutRegion} this
38393 * @param {Number} newSize The new size (width for east/west, height for north/south)
38397 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38398 this.panels = new Roo.util.MixedCollection();
38399 this.panels.getKey = this.getPanelId.createDelegate(this);
38401 this.activePanel = null;
38402 // ensure listeners are added...
38404 if (config.listeners || config.events) {
38405 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38406 listeners : config.listeners || {},
38407 events : config.events || {}
38411 if(skipConfig !== true){
38412 this.applyConfig(config);
38416 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38418 getPanelId : function(p){
38422 applyConfig : function(config){
38423 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38424 this.config = config;
38429 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38430 * the width, for horizontal (north, south) the height.
38431 * @param {Number} newSize The new width or height
38433 resizeTo : function(newSize){
38434 var el = this.el ? this.el :
38435 (this.activePanel ? this.activePanel.getEl() : null);
38437 switch(this.position){
38440 el.setWidth(newSize);
38441 this.fireEvent("resized", this, newSize);
38445 el.setHeight(newSize);
38446 this.fireEvent("resized", this, newSize);
38452 getBox : function(){
38453 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38456 getMargins : function(){
38457 return this.margins;
38460 updateBox : function(box){
38462 var el = this.activePanel.getEl();
38463 el.dom.style.left = box.x + "px";
38464 el.dom.style.top = box.y + "px";
38465 this.activePanel.setSize(box.width, box.height);
38469 * Returns the container element for this region.
38470 * @return {Roo.Element}
38472 getEl : function(){
38473 return this.activePanel;
38477 * Returns true if this region is currently visible.
38478 * @return {Boolean}
38480 isVisible : function(){
38481 return this.activePanel ? true : false;
38484 setActivePanel : function(panel){
38485 panel = this.getPanel(panel);
38486 if(this.activePanel && this.activePanel != panel){
38487 this.activePanel.setActiveState(false);
38488 this.activePanel.getEl().setLeftTop(-10000,-10000);
38490 this.activePanel = panel;
38491 panel.setActiveState(true);
38493 panel.setSize(this.box.width, this.box.height);
38495 this.fireEvent("panelactivated", this, panel);
38496 this.fireEvent("invalidated");
38500 * Show the specified panel.
38501 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38502 * @return {Roo.ContentPanel} The shown panel or null
38504 showPanel : function(panel){
38505 panel = this.getPanel(panel);
38507 this.setActivePanel(panel);
38513 * Get the active panel for this region.
38514 * @return {Roo.ContentPanel} The active panel or null
38516 getActivePanel : function(){
38517 return this.activePanel;
38521 * Add the passed ContentPanel(s)
38522 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38523 * @return {Roo.ContentPanel} The panel added (if only one was added)
38525 add : function(panel){
38526 if(arguments.length > 1){
38527 for(var i = 0, len = arguments.length; i < len; i++) {
38528 this.add(arguments[i]);
38532 if(this.hasPanel(panel)){
38533 this.showPanel(panel);
38536 var el = panel.getEl();
38537 if(el.dom.parentNode != this.mgr.el.dom){
38538 this.mgr.el.dom.appendChild(el.dom);
38540 if(panel.setRegion){
38541 panel.setRegion(this);
38543 this.panels.add(panel);
38544 el.setStyle("position", "absolute");
38545 if(!panel.background){
38546 this.setActivePanel(panel);
38547 if(this.config.initialSize && this.panels.getCount()==1){
38548 this.resizeTo(this.config.initialSize);
38551 this.fireEvent("paneladded", this, panel);
38556 * Returns true if the panel is in this region.
38557 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38558 * @return {Boolean}
38560 hasPanel : function(panel){
38561 if(typeof panel == "object"){ // must be panel obj
38562 panel = panel.getId();
38564 return this.getPanel(panel) ? true : false;
38568 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38569 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38570 * @param {Boolean} preservePanel Overrides the config preservePanel option
38571 * @return {Roo.ContentPanel} The panel that was removed
38573 remove : function(panel, preservePanel){
38574 panel = this.getPanel(panel);
38579 this.fireEvent("beforeremove", this, panel, e);
38580 if(e.cancel === true){
38583 var panelId = panel.getId();
38584 this.panels.removeKey(panelId);
38589 * Returns the panel specified or null if it's not in this region.
38590 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38591 * @return {Roo.ContentPanel}
38593 getPanel : function(id){
38594 if(typeof id == "object"){ // must be panel obj
38597 return this.panels.get(id);
38601 * Returns this regions position (north/south/east/west/center).
38604 getPosition: function(){
38605 return this.position;
38609 * Ext JS Library 1.1.1
38610 * Copyright(c) 2006-2007, Ext JS, LLC.
38612 * Originally Released Under LGPL - original licence link has changed is not relivant.
38615 * <script type="text/javascript">
38619 * @class Roo.bootstrap.layout.Region
38620 * @extends Roo.bootstrap.layout.Basic
38621 * This class represents a region in a layout manager.
38623 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38624 * @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})
38625 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38626 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38627 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38628 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38629 * @cfg {String} title The title for the region (overrides panel titles)
38630 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38631 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38632 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38633 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38634 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38635 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38636 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38637 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38638 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38639 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38641 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38642 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38643 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38644 * @cfg {Number} width For East/West panels
38645 * @cfg {Number} height For North/South panels
38646 * @cfg {Boolean} split To show the splitter
38647 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38649 * @cfg {string} cls Extra CSS classes to add to region
38651 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38652 * @cfg {string} region the region that it inhabits..
38655 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38656 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38658 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38659 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38660 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38662 Roo.bootstrap.layout.Region = function(config)
38664 this.applyConfig(config);
38666 var mgr = config.mgr;
38667 var pos = config.region;
38668 config.skipConfig = true;
38669 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38672 this.onRender(mgr.el);
38675 this.visible = true;
38676 this.collapsed = false;
38677 this.unrendered_panels = [];
38680 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38682 position: '', // set by wrapper (eg. north/south etc..)
38683 unrendered_panels : null, // unrendered panels.
38685 tabPosition : false,
38687 mgr: false, // points to 'Border'
38690 createBody : function(){
38691 /** This region's body element
38692 * @type Roo.Element */
38693 this.bodyEl = this.el.createChild({
38695 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38699 onRender: function(ctr, pos)
38701 var dh = Roo.DomHelper;
38702 /** This region's container element
38703 * @type Roo.Element */
38704 this.el = dh.append(ctr.dom, {
38706 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38708 /** This region's title element
38709 * @type Roo.Element */
38711 this.titleEl = dh.append(this.el.dom, {
38713 unselectable: "on",
38714 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38716 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38717 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38721 this.titleEl.enableDisplayMode();
38722 /** This region's title text element
38723 * @type HTMLElement */
38724 this.titleTextEl = this.titleEl.dom.firstChild;
38725 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38727 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38728 this.closeBtn.enableDisplayMode();
38729 this.closeBtn.on("click", this.closeClicked, this);
38730 this.closeBtn.hide();
38732 this.createBody(this.config);
38733 if(this.config.hideWhenEmpty){
38735 this.on("paneladded", this.validateVisibility, this);
38736 this.on("panelremoved", this.validateVisibility, this);
38738 if(this.autoScroll){
38739 this.bodyEl.setStyle("overflow", "auto");
38741 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38743 //if(c.titlebar !== false){
38744 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38745 this.titleEl.hide();
38747 this.titleEl.show();
38748 if(this.config.title){
38749 this.titleTextEl.innerHTML = this.config.title;
38753 if(this.config.collapsed){
38754 this.collapse(true);
38756 if(this.config.hidden){
38760 if (this.unrendered_panels && this.unrendered_panels.length) {
38761 for (var i =0;i< this.unrendered_panels.length; i++) {
38762 this.add(this.unrendered_panels[i]);
38764 this.unrendered_panels = null;
38770 applyConfig : function(c)
38773 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38774 var dh = Roo.DomHelper;
38775 if(c.titlebar !== false){
38776 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38777 this.collapseBtn.on("click", this.collapse, this);
38778 this.collapseBtn.enableDisplayMode();
38780 if(c.showPin === true || this.showPin){
38781 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38782 this.stickBtn.enableDisplayMode();
38783 this.stickBtn.on("click", this.expand, this);
38784 this.stickBtn.hide();
38789 /** This region's collapsed element
38790 * @type Roo.Element */
38793 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38794 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38797 if(c.floatable !== false){
38798 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38799 this.collapsedEl.on("click", this.collapseClick, this);
38802 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38803 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38804 id: "message", unselectable: "on", style:{"float":"left"}});
38805 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38807 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38808 this.expandBtn.on("click", this.expand, this);
38812 if(this.collapseBtn){
38813 this.collapseBtn.setVisible(c.collapsible == true);
38816 this.cmargins = c.cmargins || this.cmargins ||
38817 (this.position == "west" || this.position == "east" ?
38818 {top: 0, left: 2, right:2, bottom: 0} :
38819 {top: 2, left: 0, right:0, bottom: 2});
38821 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38824 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38826 this.autoScroll = c.autoScroll || false;
38831 this.duration = c.duration || .30;
38832 this.slideDuration = c.slideDuration || .45;
38837 * Returns true if this region is currently visible.
38838 * @return {Boolean}
38840 isVisible : function(){
38841 return this.visible;
38845 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38846 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38848 //setCollapsedTitle : function(title){
38849 // title = title || " ";
38850 // if(this.collapsedTitleTextEl){
38851 // this.collapsedTitleTextEl.innerHTML = title;
38855 getBox : function(){
38857 // if(!this.collapsed){
38858 b = this.el.getBox(false, true);
38860 // b = this.collapsedEl.getBox(false, true);
38865 getMargins : function(){
38866 return this.margins;
38867 //return this.collapsed ? this.cmargins : this.margins;
38870 highlight : function(){
38871 this.el.addClass("x-layout-panel-dragover");
38874 unhighlight : function(){
38875 this.el.removeClass("x-layout-panel-dragover");
38878 updateBox : function(box)
38880 if (!this.bodyEl) {
38881 return; // not rendered yet..
38885 if(!this.collapsed){
38886 this.el.dom.style.left = box.x + "px";
38887 this.el.dom.style.top = box.y + "px";
38888 this.updateBody(box.width, box.height);
38890 this.collapsedEl.dom.style.left = box.x + "px";
38891 this.collapsedEl.dom.style.top = box.y + "px";
38892 this.collapsedEl.setSize(box.width, box.height);
38895 this.tabs.autoSizeTabs();
38899 updateBody : function(w, h)
38902 this.el.setWidth(w);
38903 w -= this.el.getBorderWidth("rl");
38904 if(this.config.adjustments){
38905 w += this.config.adjustments[0];
38908 if(h !== null && h > 0){
38909 this.el.setHeight(h);
38910 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38911 h -= this.el.getBorderWidth("tb");
38912 if(this.config.adjustments){
38913 h += this.config.adjustments[1];
38915 this.bodyEl.setHeight(h);
38917 h = this.tabs.syncHeight(h);
38920 if(this.panelSize){
38921 w = w !== null ? w : this.panelSize.width;
38922 h = h !== null ? h : this.panelSize.height;
38924 if(this.activePanel){
38925 var el = this.activePanel.getEl();
38926 w = w !== null ? w : el.getWidth();
38927 h = h !== null ? h : el.getHeight();
38928 this.panelSize = {width: w, height: h};
38929 this.activePanel.setSize(w, h);
38931 if(Roo.isIE && this.tabs){
38932 this.tabs.el.repaint();
38937 * Returns the container element for this region.
38938 * @return {Roo.Element}
38940 getEl : function(){
38945 * Hides this region.
38948 //if(!this.collapsed){
38949 this.el.dom.style.left = "-2000px";
38952 // this.collapsedEl.dom.style.left = "-2000px";
38953 // this.collapsedEl.hide();
38955 this.visible = false;
38956 this.fireEvent("visibilitychange", this, false);
38960 * Shows this region if it was previously hidden.
38963 //if(!this.collapsed){
38966 // this.collapsedEl.show();
38968 this.visible = true;
38969 this.fireEvent("visibilitychange", this, true);
38972 closeClicked : function(){
38973 if(this.activePanel){
38974 this.remove(this.activePanel);
38978 collapseClick : function(e){
38980 e.stopPropagation();
38983 e.stopPropagation();
38989 * Collapses this region.
38990 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38993 collapse : function(skipAnim, skipCheck = false){
38994 if(this.collapsed) {
38998 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39000 this.collapsed = true;
39002 this.split.el.hide();
39004 if(this.config.animate && skipAnim !== true){
39005 this.fireEvent("invalidated", this);
39006 this.animateCollapse();
39008 this.el.setLocation(-20000,-20000);
39010 this.collapsedEl.show();
39011 this.fireEvent("collapsed", this);
39012 this.fireEvent("invalidated", this);
39018 animateCollapse : function(){
39023 * Expands this region if it was previously collapsed.
39024 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39025 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39028 expand : function(e, skipAnim){
39030 e.stopPropagation();
39032 if(!this.collapsed || this.el.hasActiveFx()) {
39036 this.afterSlideIn();
39039 this.collapsed = false;
39040 if(this.config.animate && skipAnim !== true){
39041 this.animateExpand();
39045 this.split.el.show();
39047 this.collapsedEl.setLocation(-2000,-2000);
39048 this.collapsedEl.hide();
39049 this.fireEvent("invalidated", this);
39050 this.fireEvent("expanded", this);
39054 animateExpand : function(){
39058 initTabs : function()
39060 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39062 var ts = new Roo.bootstrap.panel.Tabs({
39063 el: this.bodyEl.dom,
39065 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39066 disableTooltips: this.config.disableTabTips,
39067 toolbar : this.config.toolbar
39070 if(this.config.hideTabs){
39071 ts.stripWrap.setDisplayed(false);
39074 ts.resizeTabs = this.config.resizeTabs === true;
39075 ts.minTabWidth = this.config.minTabWidth || 40;
39076 ts.maxTabWidth = this.config.maxTabWidth || 250;
39077 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39078 ts.monitorResize = false;
39079 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39080 ts.bodyEl.addClass('roo-layout-tabs-body');
39081 this.panels.each(this.initPanelAsTab, this);
39084 initPanelAsTab : function(panel){
39085 var ti = this.tabs.addTab(
39089 this.config.closeOnTab && panel.isClosable(),
39092 if(panel.tabTip !== undefined){
39093 ti.setTooltip(panel.tabTip);
39095 ti.on("activate", function(){
39096 this.setActivePanel(panel);
39099 if(this.config.closeOnTab){
39100 ti.on("beforeclose", function(t, e){
39102 this.remove(panel);
39106 panel.tabItem = ti;
39111 updatePanelTitle : function(panel, title)
39113 if(this.activePanel == panel){
39114 this.updateTitle(title);
39117 var ti = this.tabs.getTab(panel.getEl().id);
39119 if(panel.tabTip !== undefined){
39120 ti.setTooltip(panel.tabTip);
39125 updateTitle : function(title){
39126 if(this.titleTextEl && !this.config.title){
39127 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39131 setActivePanel : function(panel)
39133 panel = this.getPanel(panel);
39134 if(this.activePanel && this.activePanel != panel){
39135 if(this.activePanel.setActiveState(false) === false){
39139 this.activePanel = panel;
39140 panel.setActiveState(true);
39141 if(this.panelSize){
39142 panel.setSize(this.panelSize.width, this.panelSize.height);
39145 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39147 this.updateTitle(panel.getTitle());
39149 this.fireEvent("invalidated", this);
39151 this.fireEvent("panelactivated", this, panel);
39155 * Shows the specified panel.
39156 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39157 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39159 showPanel : function(panel)
39161 panel = this.getPanel(panel);
39164 var tab = this.tabs.getTab(panel.getEl().id);
39165 if(tab.isHidden()){
39166 this.tabs.unhideTab(tab.id);
39170 this.setActivePanel(panel);
39177 * Get the active panel for this region.
39178 * @return {Roo.ContentPanel} The active panel or null
39180 getActivePanel : function(){
39181 return this.activePanel;
39184 validateVisibility : function(){
39185 if(this.panels.getCount() < 1){
39186 this.updateTitle(" ");
39187 this.closeBtn.hide();
39190 if(!this.isVisible()){
39197 * Adds the passed ContentPanel(s) to this region.
39198 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39199 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39201 add : function(panel)
39203 if(arguments.length > 1){
39204 for(var i = 0, len = arguments.length; i < len; i++) {
39205 this.add(arguments[i]);
39210 // if we have not been rendered yet, then we can not really do much of this..
39211 if (!this.bodyEl) {
39212 this.unrendered_panels.push(panel);
39219 if(this.hasPanel(panel)){
39220 this.showPanel(panel);
39223 panel.setRegion(this);
39224 this.panels.add(panel);
39225 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39226 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39227 // and hide them... ???
39228 this.bodyEl.dom.appendChild(panel.getEl().dom);
39229 if(panel.background !== true){
39230 this.setActivePanel(panel);
39232 this.fireEvent("paneladded", this, panel);
39239 this.initPanelAsTab(panel);
39243 if(panel.background !== true){
39244 this.tabs.activate(panel.getEl().id);
39246 this.fireEvent("paneladded", this, panel);
39251 * Hides the tab for the specified panel.
39252 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39254 hidePanel : function(panel){
39255 if(this.tabs && (panel = this.getPanel(panel))){
39256 this.tabs.hideTab(panel.getEl().id);
39261 * Unhides the tab for a previously hidden panel.
39262 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39264 unhidePanel : function(panel){
39265 if(this.tabs && (panel = this.getPanel(panel))){
39266 this.tabs.unhideTab(panel.getEl().id);
39270 clearPanels : function(){
39271 while(this.panels.getCount() > 0){
39272 this.remove(this.panels.first());
39277 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39278 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39279 * @param {Boolean} preservePanel Overrides the config preservePanel option
39280 * @return {Roo.ContentPanel} The panel that was removed
39282 remove : function(panel, preservePanel)
39284 panel = this.getPanel(panel);
39289 this.fireEvent("beforeremove", this, panel, e);
39290 if(e.cancel === true){
39293 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39294 var panelId = panel.getId();
39295 this.panels.removeKey(panelId);
39297 document.body.appendChild(panel.getEl().dom);
39300 this.tabs.removeTab(panel.getEl().id);
39301 }else if (!preservePanel){
39302 this.bodyEl.dom.removeChild(panel.getEl().dom);
39304 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39305 var p = this.panels.first();
39306 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39307 tempEl.appendChild(p.getEl().dom);
39308 this.bodyEl.update("");
39309 this.bodyEl.dom.appendChild(p.getEl().dom);
39311 this.updateTitle(p.getTitle());
39313 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39314 this.setActivePanel(p);
39316 panel.setRegion(null);
39317 if(this.activePanel == panel){
39318 this.activePanel = null;
39320 if(this.config.autoDestroy !== false && preservePanel !== true){
39321 try{panel.destroy();}catch(e){}
39323 this.fireEvent("panelremoved", this, panel);
39328 * Returns the TabPanel component used by this region
39329 * @return {Roo.TabPanel}
39331 getTabs : function(){
39335 createTool : function(parentEl, className){
39336 var btn = Roo.DomHelper.append(parentEl, {
39338 cls: "x-layout-tools-button",
39341 cls: "roo-layout-tools-button-inner " + className,
39345 btn.addClassOnOver("roo-layout-tools-button-over");
39350 * Ext JS Library 1.1.1
39351 * Copyright(c) 2006-2007, Ext JS, LLC.
39353 * Originally Released Under LGPL - original licence link has changed is not relivant.
39356 * <script type="text/javascript">
39362 * @class Roo.SplitLayoutRegion
39363 * @extends Roo.LayoutRegion
39364 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39366 Roo.bootstrap.layout.Split = function(config){
39367 this.cursor = config.cursor;
39368 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39371 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39373 splitTip : "Drag to resize.",
39374 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39375 useSplitTips : false,
39377 applyConfig : function(config){
39378 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39381 onRender : function(ctr,pos) {
39383 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39384 if(!this.config.split){
39389 var splitEl = Roo.DomHelper.append(ctr.dom, {
39391 id: this.el.id + "-split",
39392 cls: "roo-layout-split roo-layout-split-"+this.position,
39395 /** The SplitBar for this region
39396 * @type Roo.SplitBar */
39397 // does not exist yet...
39398 Roo.log([this.position, this.orientation]);
39400 this.split = new Roo.bootstrap.SplitBar({
39401 dragElement : splitEl,
39402 resizingElement: this.el,
39403 orientation : this.orientation
39406 this.split.on("moved", this.onSplitMove, this);
39407 this.split.useShim = this.config.useShim === true;
39408 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39409 if(this.useSplitTips){
39410 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39412 //if(config.collapsible){
39413 // this.split.el.on("dblclick", this.collapse, this);
39416 if(typeof this.config.minSize != "undefined"){
39417 this.split.minSize = this.config.minSize;
39419 if(typeof this.config.maxSize != "undefined"){
39420 this.split.maxSize = this.config.maxSize;
39422 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39423 this.hideSplitter();
39428 getHMaxSize : function(){
39429 var cmax = this.config.maxSize || 10000;
39430 var center = this.mgr.getRegion("center");
39431 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39434 getVMaxSize : function(){
39435 var cmax = this.config.maxSize || 10000;
39436 var center = this.mgr.getRegion("center");
39437 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39440 onSplitMove : function(split, newSize){
39441 this.fireEvent("resized", this, newSize);
39445 * Returns the {@link Roo.SplitBar} for this region.
39446 * @return {Roo.SplitBar}
39448 getSplitBar : function(){
39453 this.hideSplitter();
39454 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39457 hideSplitter : function(){
39459 this.split.el.setLocation(-2000,-2000);
39460 this.split.el.hide();
39466 this.split.el.show();
39468 Roo.bootstrap.layout.Split.superclass.show.call(this);
39471 beforeSlide: function(){
39472 if(Roo.isGecko){// firefox overflow auto bug workaround
39473 this.bodyEl.clip();
39475 this.tabs.bodyEl.clip();
39477 if(this.activePanel){
39478 this.activePanel.getEl().clip();
39480 if(this.activePanel.beforeSlide){
39481 this.activePanel.beforeSlide();
39487 afterSlide : function(){
39488 if(Roo.isGecko){// firefox overflow auto bug workaround
39489 this.bodyEl.unclip();
39491 this.tabs.bodyEl.unclip();
39493 if(this.activePanel){
39494 this.activePanel.getEl().unclip();
39495 if(this.activePanel.afterSlide){
39496 this.activePanel.afterSlide();
39502 initAutoHide : function(){
39503 if(this.autoHide !== false){
39504 if(!this.autoHideHd){
39505 var st = new Roo.util.DelayedTask(this.slideIn, this);
39506 this.autoHideHd = {
39507 "mouseout": function(e){
39508 if(!e.within(this.el, true)){
39512 "mouseover" : function(e){
39518 this.el.on(this.autoHideHd);
39522 clearAutoHide : function(){
39523 if(this.autoHide !== false){
39524 this.el.un("mouseout", this.autoHideHd.mouseout);
39525 this.el.un("mouseover", this.autoHideHd.mouseover);
39529 clearMonitor : function(){
39530 Roo.get(document).un("click", this.slideInIf, this);
39533 // these names are backwards but not changed for compat
39534 slideOut : function(){
39535 if(this.isSlid || this.el.hasActiveFx()){
39538 this.isSlid = true;
39539 if(this.collapseBtn){
39540 this.collapseBtn.hide();
39542 this.closeBtnState = this.closeBtn.getStyle('display');
39543 this.closeBtn.hide();
39545 this.stickBtn.show();
39548 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39549 this.beforeSlide();
39550 this.el.setStyle("z-index", 10001);
39551 this.el.slideIn(this.getSlideAnchor(), {
39552 callback: function(){
39554 this.initAutoHide();
39555 Roo.get(document).on("click", this.slideInIf, this);
39556 this.fireEvent("slideshow", this);
39563 afterSlideIn : function(){
39564 this.clearAutoHide();
39565 this.isSlid = false;
39566 this.clearMonitor();
39567 this.el.setStyle("z-index", "");
39568 if(this.collapseBtn){
39569 this.collapseBtn.show();
39571 this.closeBtn.setStyle('display', this.closeBtnState);
39573 this.stickBtn.hide();
39575 this.fireEvent("slidehide", this);
39578 slideIn : function(cb){
39579 if(!this.isSlid || this.el.hasActiveFx()){
39583 this.isSlid = false;
39584 this.beforeSlide();
39585 this.el.slideOut(this.getSlideAnchor(), {
39586 callback: function(){
39587 this.el.setLeftTop(-10000, -10000);
39589 this.afterSlideIn();
39597 slideInIf : function(e){
39598 if(!e.within(this.el)){
39603 animateCollapse : function(){
39604 this.beforeSlide();
39605 this.el.setStyle("z-index", 20000);
39606 var anchor = this.getSlideAnchor();
39607 this.el.slideOut(anchor, {
39608 callback : function(){
39609 this.el.setStyle("z-index", "");
39610 this.collapsedEl.slideIn(anchor, {duration:.3});
39612 this.el.setLocation(-10000,-10000);
39614 this.fireEvent("collapsed", this);
39621 animateExpand : function(){
39622 this.beforeSlide();
39623 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39624 this.el.setStyle("z-index", 20000);
39625 this.collapsedEl.hide({
39628 this.el.slideIn(this.getSlideAnchor(), {
39629 callback : function(){
39630 this.el.setStyle("z-index", "");
39633 this.split.el.show();
39635 this.fireEvent("invalidated", this);
39636 this.fireEvent("expanded", this);
39664 getAnchor : function(){
39665 return this.anchors[this.position];
39668 getCollapseAnchor : function(){
39669 return this.canchors[this.position];
39672 getSlideAnchor : function(){
39673 return this.sanchors[this.position];
39676 getAlignAdj : function(){
39677 var cm = this.cmargins;
39678 switch(this.position){
39694 getExpandAdj : function(){
39695 var c = this.collapsedEl, cm = this.cmargins;
39696 switch(this.position){
39698 return [-(cm.right+c.getWidth()+cm.left), 0];
39701 return [cm.right+c.getWidth()+cm.left, 0];
39704 return [0, -(cm.top+cm.bottom+c.getHeight())];
39707 return [0, cm.top+cm.bottom+c.getHeight()];
39713 * Ext JS Library 1.1.1
39714 * Copyright(c) 2006-2007, Ext JS, LLC.
39716 * Originally Released Under LGPL - original licence link has changed is not relivant.
39719 * <script type="text/javascript">
39722 * These classes are private internal classes
39724 Roo.bootstrap.layout.Center = function(config){
39725 config.region = "center";
39726 Roo.bootstrap.layout.Region.call(this, config);
39727 this.visible = true;
39728 this.minWidth = config.minWidth || 20;
39729 this.minHeight = config.minHeight || 20;
39732 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39734 // center panel can't be hidden
39738 // center panel can't be hidden
39741 getMinWidth: function(){
39742 return this.minWidth;
39745 getMinHeight: function(){
39746 return this.minHeight;
39760 Roo.bootstrap.layout.North = function(config)
39762 config.region = 'north';
39763 config.cursor = 'n-resize';
39765 Roo.bootstrap.layout.Split.call(this, config);
39769 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39770 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39771 this.split.el.addClass("roo-layout-split-v");
39773 //var size = config.initialSize || config.height;
39774 //if(this.el && typeof size != "undefined"){
39775 // this.el.setHeight(size);
39778 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39780 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39783 onRender : function(ctr, pos)
39785 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39786 var size = this.config.initialSize || this.config.height;
39787 if(this.el && typeof size != "undefined"){
39788 this.el.setHeight(size);
39793 getBox : function(){
39794 if(this.collapsed){
39795 return this.collapsedEl.getBox();
39797 var box = this.el.getBox();
39799 box.height += this.split.el.getHeight();
39804 updateBox : function(box){
39805 if(this.split && !this.collapsed){
39806 box.height -= this.split.el.getHeight();
39807 this.split.el.setLeft(box.x);
39808 this.split.el.setTop(box.y+box.height);
39809 this.split.el.setWidth(box.width);
39811 if(this.collapsed){
39812 this.updateBody(box.width, null);
39814 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39822 Roo.bootstrap.layout.South = function(config){
39823 config.region = 'south';
39824 config.cursor = 's-resize';
39825 Roo.bootstrap.layout.Split.call(this, config);
39827 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39828 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39829 this.split.el.addClass("roo-layout-split-v");
39834 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39835 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39837 onRender : function(ctr, pos)
39839 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39840 var size = this.config.initialSize || this.config.height;
39841 if(this.el && typeof size != "undefined"){
39842 this.el.setHeight(size);
39847 getBox : function(){
39848 if(this.collapsed){
39849 return this.collapsedEl.getBox();
39851 var box = this.el.getBox();
39853 var sh = this.split.el.getHeight();
39860 updateBox : function(box){
39861 if(this.split && !this.collapsed){
39862 var sh = this.split.el.getHeight();
39865 this.split.el.setLeft(box.x);
39866 this.split.el.setTop(box.y-sh);
39867 this.split.el.setWidth(box.width);
39869 if(this.collapsed){
39870 this.updateBody(box.width, null);
39872 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39876 Roo.bootstrap.layout.East = function(config){
39877 config.region = "east";
39878 config.cursor = "e-resize";
39879 Roo.bootstrap.layout.Split.call(this, config);
39881 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39882 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39883 this.split.el.addClass("roo-layout-split-h");
39887 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39888 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39890 onRender : function(ctr, pos)
39892 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39893 var size = this.config.initialSize || this.config.width;
39894 if(this.el && typeof size != "undefined"){
39895 this.el.setWidth(size);
39900 getBox : function(){
39901 if(this.collapsed){
39902 return this.collapsedEl.getBox();
39904 var box = this.el.getBox();
39906 var sw = this.split.el.getWidth();
39913 updateBox : function(box){
39914 if(this.split && !this.collapsed){
39915 var sw = this.split.el.getWidth();
39917 this.split.el.setLeft(box.x);
39918 this.split.el.setTop(box.y);
39919 this.split.el.setHeight(box.height);
39922 if(this.collapsed){
39923 this.updateBody(null, box.height);
39925 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39929 Roo.bootstrap.layout.West = function(config){
39930 config.region = "west";
39931 config.cursor = "w-resize";
39933 Roo.bootstrap.layout.Split.call(this, config);
39935 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39936 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39937 this.split.el.addClass("roo-layout-split-h");
39941 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39942 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39944 onRender: function(ctr, pos)
39946 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39947 var size = this.config.initialSize || this.config.width;
39948 if(typeof size != "undefined"){
39949 this.el.setWidth(size);
39953 getBox : function(){
39954 if(this.collapsed){
39955 return this.collapsedEl.getBox();
39957 var box = this.el.getBox();
39958 if (box.width == 0) {
39959 box.width = this.config.width; // kludge?
39962 box.width += this.split.el.getWidth();
39967 updateBox : function(box){
39968 if(this.split && !this.collapsed){
39969 var sw = this.split.el.getWidth();
39971 this.split.el.setLeft(box.x+box.width);
39972 this.split.el.setTop(box.y);
39973 this.split.el.setHeight(box.height);
39975 if(this.collapsed){
39976 this.updateBody(null, box.height);
39978 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39980 });Roo.namespace("Roo.bootstrap.panel");/*
39982 * Ext JS Library 1.1.1
39983 * Copyright(c) 2006-2007, Ext JS, LLC.
39985 * Originally Released Under LGPL - original licence link has changed is not relivant.
39988 * <script type="text/javascript">
39991 * @class Roo.ContentPanel
39992 * @extends Roo.util.Observable
39993 * A basic ContentPanel element.
39994 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39995 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39996 * @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
39997 * @cfg {Boolean} closable True if the panel can be closed/removed
39998 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39999 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40000 * @cfg {Toolbar} toolbar A toolbar for this panel
40001 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40002 * @cfg {String} title The title for this panel
40003 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40004 * @cfg {String} url Calls {@link #setUrl} with this value
40005 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40006 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40007 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40008 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40009 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40010 * @cfg {Boolean} badges render the badges
40011 * @cfg {String} cls extra classes to use
40012 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40015 * Create a new ContentPanel.
40016 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40017 * @param {String/Object} config A string to set only the title or a config object
40018 * @param {String} content (optional) Set the HTML content for this panel
40019 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40021 Roo.bootstrap.panel.Content = function( config){
40023 this.tpl = config.tpl || false;
40025 var el = config.el;
40026 var content = config.content;
40028 if(config.autoCreate){ // xtype is available if this is called from factory
40031 this.el = Roo.get(el);
40032 if(!this.el && config && config.autoCreate){
40033 if(typeof config.autoCreate == "object"){
40034 if(!config.autoCreate.id){
40035 config.autoCreate.id = config.id||el;
40037 this.el = Roo.DomHelper.append(document.body,
40038 config.autoCreate, true);
40042 cls: (config.cls || '') +
40043 (config.background ? ' bg-' + config.background : '') +
40044 " roo-layout-inactive-content",
40047 if (config.iframe) {
40051 style : 'border: 0px',
40052 src : 'about:blank'
40058 elcfg.html = config.html;
40062 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40063 if (config.iframe) {
40064 this.iframeEl = this.el.select('iframe',true).first();
40069 this.closable = false;
40070 this.loaded = false;
40071 this.active = false;
40074 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40076 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40078 this.wrapEl = this.el; //this.el.wrap();
40080 if (config.toolbar.items) {
40081 ti = config.toolbar.items ;
40082 delete config.toolbar.items ;
40086 this.toolbar.render(this.wrapEl, 'before');
40087 for(var i =0;i < ti.length;i++) {
40088 // Roo.log(['add child', items[i]]);
40089 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40091 this.toolbar.items = nitems;
40092 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40093 delete config.toolbar;
40097 // xtype created footer. - not sure if will work as we normally have to render first..
40098 if (this.footer && !this.footer.el && this.footer.xtype) {
40099 if (!this.wrapEl) {
40100 this.wrapEl = this.el.wrap();
40103 this.footer.container = this.wrapEl.createChild();
40105 this.footer = Roo.factory(this.footer, Roo);
40110 if(typeof config == "string"){
40111 this.title = config;
40113 Roo.apply(this, config);
40117 this.resizeEl = Roo.get(this.resizeEl, true);
40119 this.resizeEl = this.el;
40121 // handle view.xtype
40129 * Fires when this panel is activated.
40130 * @param {Roo.ContentPanel} this
40134 * @event deactivate
40135 * Fires when this panel is activated.
40136 * @param {Roo.ContentPanel} this
40138 "deactivate" : true,
40142 * Fires when this panel is resized if fitToFrame is true.
40143 * @param {Roo.ContentPanel} this
40144 * @param {Number} width The width after any component adjustments
40145 * @param {Number} height The height after any component adjustments
40151 * Fires when this tab is created
40152 * @param {Roo.ContentPanel} this
40163 if(this.autoScroll && !this.iframe){
40164 this.resizeEl.setStyle("overflow", "auto");
40166 // fix randome scrolling
40167 //this.el.on('scroll', function() {
40168 // Roo.log('fix random scolling');
40169 // this.scrollTo('top',0);
40172 content = content || this.content;
40174 this.setContent(content);
40176 if(config && config.url){
40177 this.setUrl(this.url, this.params, this.loadOnce);
40182 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40184 if (this.view && typeof(this.view.xtype) != 'undefined') {
40185 this.view.el = this.el.appendChild(document.createElement("div"));
40186 this.view = Roo.factory(this.view);
40187 this.view.render && this.view.render(false, '');
40191 this.fireEvent('render', this);
40194 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40204 setRegion : function(region){
40205 this.region = region;
40206 this.setActiveClass(region && !this.background);
40210 setActiveClass: function(state)
40213 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40214 this.el.setStyle('position','relative');
40216 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40217 this.el.setStyle('position', 'absolute');
40222 * Returns the toolbar for this Panel if one was configured.
40223 * @return {Roo.Toolbar}
40225 getToolbar : function(){
40226 return this.toolbar;
40229 setActiveState : function(active)
40231 this.active = active;
40232 this.setActiveClass(active);
40234 if(this.fireEvent("deactivate", this) === false){
40239 this.fireEvent("activate", this);
40243 * Updates this panel's element (not for iframe)
40244 * @param {String} content The new content
40245 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40247 setContent : function(content, loadScripts){
40252 this.el.update(content, loadScripts);
40255 ignoreResize : function(w, h){
40256 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40259 this.lastSize = {width: w, height: h};
40264 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40265 * @return {Roo.UpdateManager} The UpdateManager
40267 getUpdateManager : function(){
40271 return this.el.getUpdateManager();
40274 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40275 * Does not work with IFRAME contents
40276 * @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:
40279 url: "your-url.php",
40280 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40281 callback: yourFunction,
40282 scope: yourObject, //(optional scope)
40285 text: "Loading...",
40291 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40292 * 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.
40293 * @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}
40294 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40295 * @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.
40296 * @return {Roo.ContentPanel} this
40304 var um = this.el.getUpdateManager();
40305 um.update.apply(um, arguments);
40311 * 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.
40312 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40313 * @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)
40314 * @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)
40315 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40317 setUrl : function(url, params, loadOnce){
40319 this.iframeEl.dom.src = url;
40323 if(this.refreshDelegate){
40324 this.removeListener("activate", this.refreshDelegate);
40326 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40327 this.on("activate", this.refreshDelegate);
40328 return this.el.getUpdateManager();
40331 _handleRefresh : function(url, params, loadOnce){
40332 if(!loadOnce || !this.loaded){
40333 var updater = this.el.getUpdateManager();
40334 updater.update(url, params, this._setLoaded.createDelegate(this));
40338 _setLoaded : function(){
40339 this.loaded = true;
40343 * Returns this panel's id
40346 getId : function(){
40351 * Returns this panel's element - used by regiosn to add.
40352 * @return {Roo.Element}
40354 getEl : function(){
40355 return this.wrapEl || this.el;
40360 adjustForComponents : function(width, height)
40362 //Roo.log('adjustForComponents ');
40363 if(this.resizeEl != this.el){
40364 width -= this.el.getFrameWidth('lr');
40365 height -= this.el.getFrameWidth('tb');
40368 var te = this.toolbar.getEl();
40369 te.setWidth(width);
40370 height -= te.getHeight();
40373 var te = this.footer.getEl();
40374 te.setWidth(width);
40375 height -= te.getHeight();
40379 if(this.adjustments){
40380 width += this.adjustments[0];
40381 height += this.adjustments[1];
40383 return {"width": width, "height": height};
40386 setSize : function(width, height){
40387 if(this.fitToFrame && !this.ignoreResize(width, height)){
40388 if(this.fitContainer && this.resizeEl != this.el){
40389 this.el.setSize(width, height);
40391 var size = this.adjustForComponents(width, height);
40393 this.iframeEl.setSize(width,height);
40396 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40397 this.fireEvent('resize', this, size.width, size.height);
40404 * Returns this panel's title
40407 getTitle : function(){
40409 if (typeof(this.title) != 'object') {
40414 for (var k in this.title) {
40415 if (!this.title.hasOwnProperty(k)) {
40419 if (k.indexOf('-') >= 0) {
40420 var s = k.split('-');
40421 for (var i = 0; i<s.length; i++) {
40422 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40425 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40432 * Set this panel's title
40433 * @param {String} title
40435 setTitle : function(title){
40436 this.title = title;
40438 this.region.updatePanelTitle(this, title);
40443 * Returns true is this panel was configured to be closable
40444 * @return {Boolean}
40446 isClosable : function(){
40447 return this.closable;
40450 beforeSlide : function(){
40452 this.resizeEl.clip();
40455 afterSlide : function(){
40457 this.resizeEl.unclip();
40461 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40462 * Will fail silently if the {@link #setUrl} method has not been called.
40463 * This does not activate the panel, just updates its content.
40465 refresh : function(){
40466 if(this.refreshDelegate){
40467 this.loaded = false;
40468 this.refreshDelegate();
40473 * Destroys this panel
40475 destroy : function(){
40476 this.el.removeAllListeners();
40477 var tempEl = document.createElement("span");
40478 tempEl.appendChild(this.el.dom);
40479 tempEl.innerHTML = "";
40485 * form - if the content panel contains a form - this is a reference to it.
40486 * @type {Roo.form.Form}
40490 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40491 * This contains a reference to it.
40497 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40507 * @param {Object} cfg Xtype definition of item to add.
40511 getChildContainer: function () {
40512 return this.getEl();
40517 var ret = new Roo.factory(cfg);
40522 if (cfg.xtype.match(/^Form$/)) {
40525 //if (this.footer) {
40526 // el = this.footer.container.insertSibling(false, 'before');
40528 el = this.el.createChild();
40531 this.form = new Roo.form.Form(cfg);
40534 if ( this.form.allItems.length) {
40535 this.form.render(el.dom);
40539 // should only have one of theses..
40540 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40541 // views.. should not be just added - used named prop 'view''
40543 cfg.el = this.el.appendChild(document.createElement("div"));
40546 var ret = new Roo.factory(cfg);
40548 ret.render && ret.render(false, ''); // render blank..
40558 * @class Roo.bootstrap.panel.Grid
40559 * @extends Roo.bootstrap.panel.Content
40561 * Create a new GridPanel.
40562 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40563 * @param {Object} config A the config object
40569 Roo.bootstrap.panel.Grid = function(config)
40573 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40574 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40576 config.el = this.wrapper;
40577 //this.el = this.wrapper;
40579 if (config.container) {
40580 // ctor'ed from a Border/panel.grid
40583 this.wrapper.setStyle("overflow", "hidden");
40584 this.wrapper.addClass('roo-grid-container');
40589 if(config.toolbar){
40590 var tool_el = this.wrapper.createChild();
40591 this.toolbar = Roo.factory(config.toolbar);
40593 if (config.toolbar.items) {
40594 ti = config.toolbar.items ;
40595 delete config.toolbar.items ;
40599 this.toolbar.render(tool_el);
40600 for(var i =0;i < ti.length;i++) {
40601 // Roo.log(['add child', items[i]]);
40602 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40604 this.toolbar.items = nitems;
40606 delete config.toolbar;
40609 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40610 config.grid.scrollBody = true;;
40611 config.grid.monitorWindowResize = false; // turn off autosizing
40612 config.grid.autoHeight = false;
40613 config.grid.autoWidth = false;
40615 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40617 if (config.background) {
40618 // render grid on panel activation (if panel background)
40619 this.on('activate', function(gp) {
40620 if (!gp.grid.rendered) {
40621 gp.grid.render(this.wrapper);
40622 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40627 this.grid.render(this.wrapper);
40628 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40631 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40632 // ??? needed ??? config.el = this.wrapper;
40637 // xtype created footer. - not sure if will work as we normally have to render first..
40638 if (this.footer && !this.footer.el && this.footer.xtype) {
40640 var ctr = this.grid.getView().getFooterPanel(true);
40641 this.footer.dataSource = this.grid.dataSource;
40642 this.footer = Roo.factory(this.footer, Roo);
40643 this.footer.render(ctr);
40653 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40654 getId : function(){
40655 return this.grid.id;
40659 * Returns the grid for this panel
40660 * @return {Roo.bootstrap.Table}
40662 getGrid : function(){
40666 setSize : function(width, height){
40667 if(!this.ignoreResize(width, height)){
40668 var grid = this.grid;
40669 var size = this.adjustForComponents(width, height);
40670 // tfoot is not a footer?
40673 var gridel = grid.getGridEl();
40674 gridel.setSize(size.width, size.height);
40676 var tbd = grid.getGridEl().select('tbody', true).first();
40677 var thd = grid.getGridEl().select('thead',true).first();
40678 var tbf= grid.getGridEl().select('tfoot', true).first();
40681 size.height -= tbf.getHeight();
40684 size.height -= thd.getHeight();
40687 tbd.setSize(size.width, size.height );
40688 // this is for the account management tab -seems to work there.
40689 var thd = grid.getGridEl().select('thead',true).first();
40691 // tbd.setSize(size.width, size.height - thd.getHeight());
40700 beforeSlide : function(){
40701 this.grid.getView().scroller.clip();
40704 afterSlide : function(){
40705 this.grid.getView().scroller.unclip();
40708 destroy : function(){
40709 this.grid.destroy();
40711 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40716 * @class Roo.bootstrap.panel.Nest
40717 * @extends Roo.bootstrap.panel.Content
40719 * Create a new Panel, that can contain a layout.Border.
40722 * @param {Roo.BorderLayout} layout The layout for this panel
40723 * @param {String/Object} config A string to set only the title or a config object
40725 Roo.bootstrap.panel.Nest = function(config)
40727 // construct with only one argument..
40728 /* FIXME - implement nicer consturctors
40729 if (layout.layout) {
40731 layout = config.layout;
40732 delete config.layout;
40734 if (layout.xtype && !layout.getEl) {
40735 // then layout needs constructing..
40736 layout = Roo.factory(layout, Roo);
40740 config.el = config.layout.getEl();
40742 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40744 config.layout.monitorWindowResize = false; // turn off autosizing
40745 this.layout = config.layout;
40746 this.layout.getEl().addClass("roo-layout-nested-layout");
40747 this.layout.parent = this;
40754 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40756 setSize : function(width, height){
40757 if(!this.ignoreResize(width, height)){
40758 var size = this.adjustForComponents(width, height);
40759 var el = this.layout.getEl();
40760 if (size.height < 1) {
40761 el.setWidth(size.width);
40763 el.setSize(size.width, size.height);
40765 var touch = el.dom.offsetWidth;
40766 this.layout.layout();
40767 // ie requires a double layout on the first pass
40768 if(Roo.isIE && !this.initialized){
40769 this.initialized = true;
40770 this.layout.layout();
40775 // activate all subpanels if not currently active..
40777 setActiveState : function(active){
40778 this.active = active;
40779 this.setActiveClass(active);
40782 this.fireEvent("deactivate", this);
40786 this.fireEvent("activate", this);
40787 // not sure if this should happen before or after..
40788 if (!this.layout) {
40789 return; // should not happen..
40792 for (var r in this.layout.regions) {
40793 reg = this.layout.getRegion(r);
40794 if (reg.getActivePanel()) {
40795 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40796 reg.setActivePanel(reg.getActivePanel());
40799 if (!reg.panels.length) {
40802 reg.showPanel(reg.getPanel(0));
40811 * Returns the nested BorderLayout for this panel
40812 * @return {Roo.BorderLayout}
40814 getLayout : function(){
40815 return this.layout;
40819 * Adds a xtype elements to the layout of the nested panel
40823 xtype : 'ContentPanel',
40830 xtype : 'NestedLayoutPanel',
40836 items : [ ... list of content panels or nested layout panels.. ]
40840 * @param {Object} cfg Xtype definition of item to add.
40842 addxtype : function(cfg) {
40843 return this.layout.addxtype(cfg);
40848 * Ext JS Library 1.1.1
40849 * Copyright(c) 2006-2007, Ext JS, LLC.
40851 * Originally Released Under LGPL - original licence link has changed is not relivant.
40854 * <script type="text/javascript">
40857 * @class Roo.TabPanel
40858 * @extends Roo.util.Observable
40859 * A lightweight tab container.
40863 // basic tabs 1, built from existing content
40864 var tabs = new Roo.TabPanel("tabs1");
40865 tabs.addTab("script", "View Script");
40866 tabs.addTab("markup", "View Markup");
40867 tabs.activate("script");
40869 // more advanced tabs, built from javascript
40870 var jtabs = new Roo.TabPanel("jtabs");
40871 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40873 // set up the UpdateManager
40874 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40875 var updater = tab2.getUpdateManager();
40876 updater.setDefaultUrl("ajax1.htm");
40877 tab2.on('activate', updater.refresh, updater, true);
40879 // Use setUrl for Ajax loading
40880 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40881 tab3.setUrl("ajax2.htm", null, true);
40884 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40887 jtabs.activate("jtabs-1");
40890 * Create a new TabPanel.
40891 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40892 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40894 Roo.bootstrap.panel.Tabs = function(config){
40896 * The container element for this TabPanel.
40897 * @type Roo.Element
40899 this.el = Roo.get(config.el);
40902 if(typeof config == "boolean"){
40903 this.tabPosition = config ? "bottom" : "top";
40905 Roo.apply(this, config);
40909 if(this.tabPosition == "bottom"){
40910 // if tabs are at the bottom = create the body first.
40911 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40912 this.el.addClass("roo-tabs-bottom");
40914 // next create the tabs holders
40916 if (this.tabPosition == "west"){
40918 var reg = this.region; // fake it..
40920 if (!reg.mgr.parent) {
40923 reg = reg.mgr.parent.region;
40925 Roo.log("got nest?");
40927 if (reg.mgr.getRegion('west')) {
40928 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40929 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40930 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40931 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40932 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40940 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40941 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40942 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40943 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40948 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40951 // finally - if tabs are at the top, then create the body last..
40952 if(this.tabPosition != "bottom"){
40953 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40954 * @type Roo.Element
40956 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40957 this.el.addClass("roo-tabs-top");
40961 this.bodyEl.setStyle("position", "relative");
40963 this.active = null;
40964 this.activateDelegate = this.activate.createDelegate(this);
40969 * Fires when the active tab changes
40970 * @param {Roo.TabPanel} this
40971 * @param {Roo.TabPanelItem} activePanel The new active tab
40975 * @event beforetabchange
40976 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40977 * @param {Roo.TabPanel} this
40978 * @param {Object} e Set cancel to true on this object to cancel the tab change
40979 * @param {Roo.TabPanelItem} tab The tab being changed to
40981 "beforetabchange" : true
40984 Roo.EventManager.onWindowResize(this.onResize, this);
40985 this.cpad = this.el.getPadding("lr");
40986 this.hiddenCount = 0;
40989 // toolbar on the tabbar support...
40990 if (this.toolbar) {
40991 alert("no toolbar support yet");
40992 this.toolbar = false;
40994 var tcfg = this.toolbar;
40995 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40996 this.toolbar = new Roo.Toolbar(tcfg);
40997 if (Roo.isSafari) {
40998 var tbl = tcfg.container.child('table', true);
40999 tbl.setAttribute('width', '100%');
41007 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41010 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41012 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41014 tabPosition : "top",
41016 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41018 currentTabWidth : 0,
41020 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41024 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41028 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41030 preferredTabWidth : 175,
41032 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41034 resizeTabs : false,
41036 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41038 monitorResize : true,
41040 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41042 toolbar : false, // set by caller..
41044 region : false, /// set by caller
41046 disableTooltips : true, // not used yet...
41049 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41050 * @param {String} id The id of the div to use <b>or create</b>
41051 * @param {String} text The text for the tab
41052 * @param {String} content (optional) Content to put in the TabPanelItem body
41053 * @param {Boolean} closable (optional) True to create a close icon on the tab
41054 * @return {Roo.TabPanelItem} The created TabPanelItem
41056 addTab : function(id, text, content, closable, tpl)
41058 var item = new Roo.bootstrap.panel.TabItem({
41062 closable : closable,
41065 this.addTabItem(item);
41067 item.setContent(content);
41073 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41074 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41075 * @return {Roo.TabPanelItem}
41077 getTab : function(id){
41078 return this.items[id];
41082 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41083 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41085 hideTab : function(id){
41086 var t = this.items[id];
41089 this.hiddenCount++;
41090 this.autoSizeTabs();
41095 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41096 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41098 unhideTab : function(id){
41099 var t = this.items[id];
41101 t.setHidden(false);
41102 this.hiddenCount--;
41103 this.autoSizeTabs();
41108 * Adds an existing {@link Roo.TabPanelItem}.
41109 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41111 addTabItem : function(item)
41113 this.items[item.id] = item;
41114 this.items.push(item);
41115 this.autoSizeTabs();
41116 // if(this.resizeTabs){
41117 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41118 // this.autoSizeTabs();
41120 // item.autoSize();
41125 * Removes a {@link Roo.TabPanelItem}.
41126 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41128 removeTab : function(id){
41129 var items = this.items;
41130 var tab = items[id];
41131 if(!tab) { return; }
41132 var index = items.indexOf(tab);
41133 if(this.active == tab && items.length > 1){
41134 var newTab = this.getNextAvailable(index);
41139 this.stripEl.dom.removeChild(tab.pnode.dom);
41140 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41141 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41143 items.splice(index, 1);
41144 delete this.items[tab.id];
41145 tab.fireEvent("close", tab);
41146 tab.purgeListeners();
41147 this.autoSizeTabs();
41150 getNextAvailable : function(start){
41151 var items = this.items;
41153 // look for a next tab that will slide over to
41154 // replace the one being removed
41155 while(index < items.length){
41156 var item = items[++index];
41157 if(item && !item.isHidden()){
41161 // if one isn't found select the previous tab (on the left)
41164 var item = items[--index];
41165 if(item && !item.isHidden()){
41173 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41174 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41176 disableTab : function(id){
41177 var tab = this.items[id];
41178 if(tab && this.active != tab){
41184 * Enables a {@link Roo.TabPanelItem} that is disabled.
41185 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41187 enableTab : function(id){
41188 var tab = this.items[id];
41193 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41194 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41195 * @return {Roo.TabPanelItem} The TabPanelItem.
41197 activate : function(id)
41199 //Roo.log('activite:' + id);
41201 var tab = this.items[id];
41205 if(tab == this.active || tab.disabled){
41209 this.fireEvent("beforetabchange", this, e, tab);
41210 if(e.cancel !== true && !tab.disabled){
41212 this.active.hide();
41214 this.active = this.items[id];
41215 this.active.show();
41216 this.fireEvent("tabchange", this, this.active);
41222 * Gets the active {@link Roo.TabPanelItem}.
41223 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41225 getActiveTab : function(){
41226 return this.active;
41230 * Updates the tab body element to fit the height of the container element
41231 * for overflow scrolling
41232 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41234 syncHeight : function(targetHeight){
41235 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41236 var bm = this.bodyEl.getMargins();
41237 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41238 this.bodyEl.setHeight(newHeight);
41242 onResize : function(){
41243 if(this.monitorResize){
41244 this.autoSizeTabs();
41249 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41251 beginUpdate : function(){
41252 this.updating = true;
41256 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41258 endUpdate : function(){
41259 this.updating = false;
41260 this.autoSizeTabs();
41264 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41266 autoSizeTabs : function()
41268 var count = this.items.length;
41269 var vcount = count - this.hiddenCount;
41272 this.stripEl.hide();
41274 this.stripEl.show();
41277 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41282 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41283 var availWidth = Math.floor(w / vcount);
41284 var b = this.stripBody;
41285 if(b.getWidth() > w){
41286 var tabs = this.items;
41287 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41288 if(availWidth < this.minTabWidth){
41289 /*if(!this.sleft){ // incomplete scrolling code
41290 this.createScrollButtons();
41293 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41296 if(this.currentTabWidth < this.preferredTabWidth){
41297 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41303 * Returns the number of tabs in this TabPanel.
41306 getCount : function(){
41307 return this.items.length;
41311 * Resizes all the tabs to the passed width
41312 * @param {Number} The new width
41314 setTabWidth : function(width){
41315 this.currentTabWidth = width;
41316 for(var i = 0, len = this.items.length; i < len; i++) {
41317 if(!this.items[i].isHidden()) {
41318 this.items[i].setWidth(width);
41324 * Destroys this TabPanel
41325 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41327 destroy : function(removeEl){
41328 Roo.EventManager.removeResizeListener(this.onResize, this);
41329 for(var i = 0, len = this.items.length; i < len; i++){
41330 this.items[i].purgeListeners();
41332 if(removeEl === true){
41333 this.el.update("");
41338 createStrip : function(container)
41340 var strip = document.createElement("nav");
41341 strip.className = Roo.bootstrap.version == 4 ?
41342 "navbar-light bg-light" :
41343 "navbar navbar-default"; //"x-tabs-wrap";
41344 container.appendChild(strip);
41348 createStripList : function(strip)
41350 // div wrapper for retard IE
41351 // returns the "tr" element.
41352 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41353 //'<div class="x-tabs-strip-wrap">'+
41354 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41355 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41356 return strip.firstChild; //.firstChild.firstChild.firstChild;
41358 createBody : function(container)
41360 var body = document.createElement("div");
41361 Roo.id(body, "tab-body");
41362 //Roo.fly(body).addClass("x-tabs-body");
41363 Roo.fly(body).addClass("tab-content");
41364 container.appendChild(body);
41367 createItemBody :function(bodyEl, id){
41368 var body = Roo.getDom(id);
41370 body = document.createElement("div");
41373 //Roo.fly(body).addClass("x-tabs-item-body");
41374 Roo.fly(body).addClass("tab-pane");
41375 bodyEl.insertBefore(body, bodyEl.firstChild);
41379 createStripElements : function(stripEl, text, closable, tpl)
41381 var td = document.createElement("li"); // was td..
41382 td.className = 'nav-item';
41384 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41387 stripEl.appendChild(td);
41389 td.className = "x-tabs-closable";
41390 if(!this.closeTpl){
41391 this.closeTpl = new Roo.Template(
41392 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41393 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41394 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41397 var el = this.closeTpl.overwrite(td, {"text": text});
41398 var close = el.getElementsByTagName("div")[0];
41399 var inner = el.getElementsByTagName("em")[0];
41400 return {"el": el, "close": close, "inner": inner};
41403 // not sure what this is..
41404 // if(!this.tabTpl){
41405 //this.tabTpl = new Roo.Template(
41406 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41407 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41409 // this.tabTpl = new Roo.Template(
41410 // '<a href="#">' +
41411 // '<span unselectable="on"' +
41412 // (this.disableTooltips ? '' : ' title="{text}"') +
41413 // ' >{text}</span></a>'
41419 var template = tpl || this.tabTpl || false;
41422 template = new Roo.Template(
41423 Roo.bootstrap.version == 4 ?
41425 '<a class="nav-link" href="#" unselectable="on"' +
41426 (this.disableTooltips ? '' : ' title="{text}"') +
41429 '<a class="nav-link" href="#">' +
41430 '<span unselectable="on"' +
41431 (this.disableTooltips ? '' : ' title="{text}"') +
41432 ' >{text}</span></a>'
41437 switch (typeof(template)) {
41441 template = new Roo.Template(template);
41447 var el = template.overwrite(td, {"text": text});
41449 var inner = el.getElementsByTagName("span")[0];
41451 return {"el": el, "inner": inner};
41459 * @class Roo.TabPanelItem
41460 * @extends Roo.util.Observable
41461 * Represents an individual item (tab plus body) in a TabPanel.
41462 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41463 * @param {String} id The id of this TabPanelItem
41464 * @param {String} text The text for the tab of this TabPanelItem
41465 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41467 Roo.bootstrap.panel.TabItem = function(config){
41469 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41470 * @type Roo.TabPanel
41472 this.tabPanel = config.panel;
41474 * The id for this TabPanelItem
41477 this.id = config.id;
41479 this.disabled = false;
41481 this.text = config.text;
41483 this.loaded = false;
41484 this.closable = config.closable;
41487 * The body element for this TabPanelItem.
41488 * @type Roo.Element
41490 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41491 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41492 this.bodyEl.setStyle("display", "block");
41493 this.bodyEl.setStyle("zoom", "1");
41494 //this.hideAction();
41496 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41498 this.el = Roo.get(els.el);
41499 this.inner = Roo.get(els.inner, true);
41500 this.textEl = Roo.bootstrap.version == 4 ?
41501 this.el : Roo.get(this.el.dom.firstChild, true);
41503 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41504 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41507 // this.el.on("mousedown", this.onTabMouseDown, this);
41508 this.el.on("click", this.onTabClick, this);
41510 if(config.closable){
41511 var c = Roo.get(els.close, true);
41512 c.dom.title = this.closeText;
41513 c.addClassOnOver("close-over");
41514 c.on("click", this.closeClick, this);
41520 * Fires when this tab becomes the active tab.
41521 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41522 * @param {Roo.TabPanelItem} this
41526 * @event beforeclose
41527 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41528 * @param {Roo.TabPanelItem} this
41529 * @param {Object} e Set cancel to true on this object to cancel the close.
41531 "beforeclose": true,
41534 * Fires when this tab is closed.
41535 * @param {Roo.TabPanelItem} this
41539 * @event deactivate
41540 * Fires when this tab is no longer the active tab.
41541 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41542 * @param {Roo.TabPanelItem} this
41544 "deactivate" : true
41546 this.hidden = false;
41548 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41551 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41553 purgeListeners : function(){
41554 Roo.util.Observable.prototype.purgeListeners.call(this);
41555 this.el.removeAllListeners();
41558 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41561 this.status_node.addClass("active");
41564 this.tabPanel.stripWrap.repaint();
41566 this.fireEvent("activate", this.tabPanel, this);
41570 * Returns true if this tab is the active tab.
41571 * @return {Boolean}
41573 isActive : function(){
41574 return this.tabPanel.getActiveTab() == this;
41578 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41581 this.status_node.removeClass("active");
41583 this.fireEvent("deactivate", this.tabPanel, this);
41586 hideAction : function(){
41587 this.bodyEl.hide();
41588 this.bodyEl.setStyle("position", "absolute");
41589 this.bodyEl.setLeft("-20000px");
41590 this.bodyEl.setTop("-20000px");
41593 showAction : function(){
41594 this.bodyEl.setStyle("position", "relative");
41595 this.bodyEl.setTop("");
41596 this.bodyEl.setLeft("");
41597 this.bodyEl.show();
41601 * Set the tooltip for the tab.
41602 * @param {String} tooltip The tab's tooltip
41604 setTooltip : function(text){
41605 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41606 this.textEl.dom.qtip = text;
41607 this.textEl.dom.removeAttribute('title');
41609 this.textEl.dom.title = text;
41613 onTabClick : function(e){
41614 e.preventDefault();
41615 this.tabPanel.activate(this.id);
41618 onTabMouseDown : function(e){
41619 e.preventDefault();
41620 this.tabPanel.activate(this.id);
41623 getWidth : function(){
41624 return this.inner.getWidth();
41627 setWidth : function(width){
41628 var iwidth = width - this.linode.getPadding("lr");
41629 this.inner.setWidth(iwidth);
41630 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41631 this.linode.setWidth(width);
41635 * Show or hide the tab
41636 * @param {Boolean} hidden True to hide or false to show.
41638 setHidden : function(hidden){
41639 this.hidden = hidden;
41640 this.linode.setStyle("display", hidden ? "none" : "");
41644 * Returns true if this tab is "hidden"
41645 * @return {Boolean}
41647 isHidden : function(){
41648 return this.hidden;
41652 * Returns the text for this tab
41655 getText : function(){
41659 autoSize : function(){
41660 //this.el.beginMeasure();
41661 this.textEl.setWidth(1);
41663 * #2804 [new] Tabs in Roojs
41664 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41666 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41667 //this.el.endMeasure();
41671 * Sets the text for the tab (Note: this also sets the tooltip text)
41672 * @param {String} text The tab's text and tooltip
41674 setText : function(text){
41676 this.textEl.update(text);
41677 this.setTooltip(text);
41678 //if(!this.tabPanel.resizeTabs){
41679 // this.autoSize();
41683 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41685 activate : function(){
41686 this.tabPanel.activate(this.id);
41690 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41692 disable : function(){
41693 if(this.tabPanel.active != this){
41694 this.disabled = true;
41695 this.status_node.addClass("disabled");
41700 * Enables this TabPanelItem if it was previously disabled.
41702 enable : function(){
41703 this.disabled = false;
41704 this.status_node.removeClass("disabled");
41708 * Sets the content for this TabPanelItem.
41709 * @param {String} content The content
41710 * @param {Boolean} loadScripts true to look for and load scripts
41712 setContent : function(content, loadScripts){
41713 this.bodyEl.update(content, loadScripts);
41717 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41718 * @return {Roo.UpdateManager} The UpdateManager
41720 getUpdateManager : function(){
41721 return this.bodyEl.getUpdateManager();
41725 * Set a URL to be used to load the content for this TabPanelItem.
41726 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41727 * @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)
41728 * @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)
41729 * @return {Roo.UpdateManager} The UpdateManager
41731 setUrl : function(url, params, loadOnce){
41732 if(this.refreshDelegate){
41733 this.un('activate', this.refreshDelegate);
41735 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41736 this.on("activate", this.refreshDelegate);
41737 return this.bodyEl.getUpdateManager();
41741 _handleRefresh : function(url, params, loadOnce){
41742 if(!loadOnce || !this.loaded){
41743 var updater = this.bodyEl.getUpdateManager();
41744 updater.update(url, params, this._setLoaded.createDelegate(this));
41749 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41750 * Will fail silently if the setUrl method has not been called.
41751 * This does not activate the panel, just updates its content.
41753 refresh : function(){
41754 if(this.refreshDelegate){
41755 this.loaded = false;
41756 this.refreshDelegate();
41761 _setLoaded : function(){
41762 this.loaded = true;
41766 closeClick : function(e){
41769 this.fireEvent("beforeclose", this, o);
41770 if(o.cancel !== true){
41771 this.tabPanel.removeTab(this.id);
41775 * The text displayed in the tooltip for the close icon.
41778 closeText : "Close this tab"
41781 * This script refer to:
41782 * Title: International Telephone Input
41783 * Author: Jack O'Connor
41784 * Code version: v12.1.12
41785 * Availability: https://github.com/jackocnr/intl-tel-input.git
41788 Roo.bootstrap.PhoneInputData = function() {
41791 "Afghanistan (افغانستان)",
41796 "Albania (Shqipëri)",
41801 "Algeria (الجزائر)",
41826 "Antigua and Barbuda",
41836 "Armenia (Հայաստան)",
41852 "Austria (Österreich)",
41857 "Azerbaijan (Azərbaycan)",
41867 "Bahrain (البحرين)",
41872 "Bangladesh (বাংলাদেশ)",
41882 "Belarus (Беларусь)",
41887 "Belgium (België)",
41917 "Bosnia and Herzegovina (Босна и Херцеговина)",
41932 "British Indian Ocean Territory",
41937 "British Virgin Islands",
41947 "Bulgaria (България)",
41957 "Burundi (Uburundi)",
41962 "Cambodia (កម្ពុជា)",
41967 "Cameroon (Cameroun)",
41976 ["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"]
41979 "Cape Verde (Kabu Verdi)",
41984 "Caribbean Netherlands",
41995 "Central African Republic (République centrafricaine)",
42015 "Christmas Island",
42021 "Cocos (Keeling) Islands",
42032 "Comoros (جزر القمر)",
42037 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42042 "Congo (Republic) (Congo-Brazzaville)",
42062 "Croatia (Hrvatska)",
42083 "Czech Republic (Česká republika)",
42088 "Denmark (Danmark)",
42103 "Dominican Republic (República Dominicana)",
42107 ["809", "829", "849"]
42125 "Equatorial Guinea (Guinea Ecuatorial)",
42145 "Falkland Islands (Islas Malvinas)",
42150 "Faroe Islands (Føroyar)",
42171 "French Guiana (Guyane française)",
42176 "French Polynesia (Polynésie française)",
42191 "Georgia (საქართველო)",
42196 "Germany (Deutschland)",
42216 "Greenland (Kalaallit Nunaat)",
42253 "Guinea-Bissau (Guiné Bissau)",
42278 "Hungary (Magyarország)",
42283 "Iceland (Ísland)",
42303 "Iraq (العراق)",
42319 "Israel (ישראל)",
42346 "Jordan (الأردن)",
42351 "Kazakhstan (Казахстан)",
42372 "Kuwait (الكويت)",
42377 "Kyrgyzstan (Кыргызстан)",
42387 "Latvia (Latvija)",
42392 "Lebanon (لبنان)",
42407 "Libya (ليبيا)",
42417 "Lithuania (Lietuva)",
42432 "Macedonia (FYROM) (Македонија)",
42437 "Madagascar (Madagasikara)",
42467 "Marshall Islands",
42477 "Mauritania (موريتانيا)",
42482 "Mauritius (Moris)",
42503 "Moldova (Republica Moldova)",
42513 "Mongolia (Монгол)",
42518 "Montenegro (Crna Gora)",
42528 "Morocco (المغرب)",
42534 "Mozambique (Moçambique)",
42539 "Myanmar (Burma) (မြန်မာ)",
42544 "Namibia (Namibië)",
42559 "Netherlands (Nederland)",
42564 "New Caledonia (Nouvelle-Calédonie)",
42599 "North Korea (조선 민주주의 인민 공화국)",
42604 "Northern Mariana Islands",
42620 "Pakistan (پاکستان)",
42630 "Palestine (فلسطين)",
42640 "Papua New Guinea",
42682 "Réunion (La Réunion)",
42688 "Romania (România)",
42704 "Saint Barthélemy",
42715 "Saint Kitts and Nevis",
42725 "Saint Martin (Saint-Martin (partie française))",
42731 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42736 "Saint Vincent and the Grenadines",
42751 "São Tomé and Príncipe (São Tomé e Príncipe)",
42756 "Saudi Arabia (المملكة العربية السعودية)",
42761 "Senegal (Sénégal)",
42791 "Slovakia (Slovensko)",
42796 "Slovenia (Slovenija)",
42806 "Somalia (Soomaaliya)",
42816 "South Korea (대한민국)",
42821 "South Sudan (جنوب السودان)",
42831 "Sri Lanka (ශ්රී ලංකාව)",
42836 "Sudan (السودان)",
42846 "Svalbard and Jan Mayen",
42857 "Sweden (Sverige)",
42862 "Switzerland (Schweiz)",
42867 "Syria (سوريا)",
42912 "Trinidad and Tobago",
42917 "Tunisia (تونس)",
42922 "Turkey (Türkiye)",
42932 "Turks and Caicos Islands",
42942 "U.S. Virgin Islands",
42952 "Ukraine (Україна)",
42957 "United Arab Emirates (الإمارات العربية المتحدة)",
42979 "Uzbekistan (Oʻzbekiston)",
42989 "Vatican City (Città del Vaticano)",
43000 "Vietnam (Việt Nam)",
43005 "Wallis and Futuna (Wallis-et-Futuna)",
43010 "Western Sahara (الصحراء الغربية)",
43016 "Yemen (اليمن)",
43040 * This script refer to:
43041 * Title: International Telephone Input
43042 * Author: Jack O'Connor
43043 * Code version: v12.1.12
43044 * Availability: https://github.com/jackocnr/intl-tel-input.git
43048 * @class Roo.bootstrap.PhoneInput
43049 * @extends Roo.bootstrap.TriggerField
43050 * An input with International dial-code selection
43052 * @cfg {String} defaultDialCode default '+852'
43053 * @cfg {Array} preferedCountries default []
43056 * Create a new PhoneInput.
43057 * @param {Object} config Configuration options
43060 Roo.bootstrap.PhoneInput = function(config) {
43061 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43064 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43066 listWidth: undefined,
43068 selectedClass: 'active',
43070 invalidClass : "has-warning",
43072 validClass: 'has-success',
43074 allowed: '0123456789',
43079 * @cfg {String} defaultDialCode The default dial code when initializing the input
43081 defaultDialCode: '+852',
43084 * @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
43086 preferedCountries: false,
43088 getAutoCreate : function()
43090 var data = Roo.bootstrap.PhoneInputData();
43091 var align = this.labelAlign || this.parentLabelAlign();
43094 this.allCountries = [];
43095 this.dialCodeMapping = [];
43097 for (var i = 0; i < data.length; i++) {
43099 this.allCountries[i] = {
43103 priority: c[3] || 0,
43104 areaCodes: c[4] || null
43106 this.dialCodeMapping[c[2]] = {
43109 priority: c[3] || 0,
43110 areaCodes: c[4] || null
43122 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43123 maxlength: this.max_length,
43124 cls : 'form-control tel-input',
43125 autocomplete: 'new-password'
43128 var hiddenInput = {
43131 cls: 'hidden-tel-input'
43135 hiddenInput.name = this.name;
43138 if (this.disabled) {
43139 input.disabled = true;
43142 var flag_container = {
43159 cls: this.hasFeedback ? 'has-feedback' : '',
43165 cls: 'dial-code-holder',
43172 cls: 'roo-select2-container input-group',
43179 if (this.fieldLabel.length) {
43182 tooltip: 'This field is required'
43188 cls: 'control-label',
43194 html: this.fieldLabel
43197 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43203 if(this.indicatorpos == 'right') {
43204 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43211 if(align == 'left') {
43219 if(this.labelWidth > 12){
43220 label.style = "width: " + this.labelWidth + 'px';
43222 if(this.labelWidth < 13 && this.labelmd == 0){
43223 this.labelmd = this.labelWidth;
43225 if(this.labellg > 0){
43226 label.cls += ' col-lg-' + this.labellg;
43227 input.cls += ' col-lg-' + (12 - this.labellg);
43229 if(this.labelmd > 0){
43230 label.cls += ' col-md-' + this.labelmd;
43231 container.cls += ' col-md-' + (12 - this.labelmd);
43233 if(this.labelsm > 0){
43234 label.cls += ' col-sm-' + this.labelsm;
43235 container.cls += ' col-sm-' + (12 - this.labelsm);
43237 if(this.labelxs > 0){
43238 label.cls += ' col-xs-' + this.labelxs;
43239 container.cls += ' col-xs-' + (12 - this.labelxs);
43249 var settings = this;
43251 ['xs','sm','md','lg'].map(function(size){
43252 if (settings[size]) {
43253 cfg.cls += ' col-' + size + '-' + settings[size];
43257 this.store = new Roo.data.Store({
43258 proxy : new Roo.data.MemoryProxy({}),
43259 reader : new Roo.data.JsonReader({
43270 'name' : 'dialCode',
43274 'name' : 'priority',
43278 'name' : 'areaCodes',
43285 if(!this.preferedCountries) {
43286 this.preferedCountries = [
43293 var p = this.preferedCountries.reverse();
43296 for (var i = 0; i < p.length; i++) {
43297 for (var j = 0; j < this.allCountries.length; j++) {
43298 if(this.allCountries[j].iso2 == p[i]) {
43299 var t = this.allCountries[j];
43300 this.allCountries.splice(j,1);
43301 this.allCountries.unshift(t);
43307 this.store.proxy.data = {
43309 data: this.allCountries
43315 initEvents : function()
43318 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43320 this.indicator = this.indicatorEl();
43321 this.flag = this.flagEl();
43322 this.dialCodeHolder = this.dialCodeHolderEl();
43324 this.trigger = this.el.select('div.flag-box',true).first();
43325 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43330 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43331 _this.list.setWidth(lw);
43334 this.list.on('mouseover', this.onViewOver, this);
43335 this.list.on('mousemove', this.onViewMove, this);
43336 this.inputEl().on("keyup", this.onKeyUp, this);
43337 this.inputEl().on("keypress", this.onKeyPress, this);
43339 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43341 this.view = new Roo.View(this.list, this.tpl, {
43342 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43345 this.view.on('click', this.onViewClick, this);
43346 this.setValue(this.defaultDialCode);
43349 onTriggerClick : function(e)
43351 Roo.log('trigger click');
43356 if(this.isExpanded()){
43358 this.hasFocus = false;
43360 this.store.load({});
43361 this.hasFocus = true;
43366 isExpanded : function()
43368 return this.list.isVisible();
43371 collapse : function()
43373 if(!this.isExpanded()){
43377 Roo.get(document).un('mousedown', this.collapseIf, this);
43378 Roo.get(document).un('mousewheel', this.collapseIf, this);
43379 this.fireEvent('collapse', this);
43383 expand : function()
43387 if(this.isExpanded() || !this.hasFocus){
43391 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43392 this.list.setWidth(lw);
43395 this.restrictHeight();
43397 Roo.get(document).on('mousedown', this.collapseIf, this);
43398 Roo.get(document).on('mousewheel', this.collapseIf, this);
43400 this.fireEvent('expand', this);
43403 restrictHeight : function()
43405 this.list.alignTo(this.inputEl(), this.listAlign);
43406 this.list.alignTo(this.inputEl(), this.listAlign);
43409 onViewOver : function(e, t)
43411 if(this.inKeyMode){
43414 var item = this.view.findItemFromChild(t);
43417 var index = this.view.indexOf(item);
43418 this.select(index, false);
43423 onViewClick : function(view, doFocus, el, e)
43425 var index = this.view.getSelectedIndexes()[0];
43427 var r = this.store.getAt(index);
43430 this.onSelect(r, index);
43432 if(doFocus !== false && !this.blockFocus){
43433 this.inputEl().focus();
43437 onViewMove : function(e, t)
43439 this.inKeyMode = false;
43442 select : function(index, scrollIntoView)
43444 this.selectedIndex = index;
43445 this.view.select(index);
43446 if(scrollIntoView !== false){
43447 var el = this.view.getNode(index);
43449 this.list.scrollChildIntoView(el, false);
43454 createList : function()
43456 this.list = Roo.get(document.body).createChild({
43458 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43459 style: 'display:none'
43462 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43465 collapseIf : function(e)
43467 var in_combo = e.within(this.el);
43468 var in_list = e.within(this.list);
43469 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43471 if (in_combo || in_list || is_list) {
43477 onSelect : function(record, index)
43479 if(this.fireEvent('beforeselect', this, record, index) !== false){
43481 this.setFlagClass(record.data.iso2);
43482 this.setDialCode(record.data.dialCode);
43483 this.hasFocus = false;
43485 this.fireEvent('select', this, record, index);
43489 flagEl : function()
43491 var flag = this.el.select('div.flag',true).first();
43498 dialCodeHolderEl : function()
43500 var d = this.el.select('input.dial-code-holder',true).first();
43507 setDialCode : function(v)
43509 this.dialCodeHolder.dom.value = '+'+v;
43512 setFlagClass : function(n)
43514 this.flag.dom.className = 'flag '+n;
43517 getValue : function()
43519 var v = this.inputEl().getValue();
43520 if(this.dialCodeHolder) {
43521 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43526 setValue : function(v)
43528 var d = this.getDialCode(v);
43530 //invalid dial code
43531 if(v.length == 0 || !d || d.length == 0) {
43533 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43534 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43540 this.setFlagClass(this.dialCodeMapping[d].iso2);
43541 this.setDialCode(d);
43542 this.inputEl().dom.value = v.replace('+'+d,'');
43543 this.hiddenEl().dom.value = this.getValue();
43548 getDialCode : function(v)
43552 if (v.length == 0) {
43553 return this.dialCodeHolder.dom.value;
43557 if (v.charAt(0) != "+") {
43560 var numericChars = "";
43561 for (var i = 1; i < v.length; i++) {
43562 var c = v.charAt(i);
43565 if (this.dialCodeMapping[numericChars]) {
43566 dialCode = v.substr(1, i);
43568 if (numericChars.length == 4) {
43578 this.setValue(this.defaultDialCode);
43582 hiddenEl : function()
43584 return this.el.select('input.hidden-tel-input',true).first();
43587 // after setting val
43588 onKeyUp : function(e){
43589 this.setValue(this.getValue());
43592 onKeyPress : function(e){
43593 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43600 * @class Roo.bootstrap.MoneyField
43601 * @extends Roo.bootstrap.ComboBox
43602 * Bootstrap MoneyField class
43605 * Create a new MoneyField.
43606 * @param {Object} config Configuration options
43609 Roo.bootstrap.MoneyField = function(config) {
43611 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43615 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43618 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43620 allowDecimals : true,
43622 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43624 decimalSeparator : ".",
43626 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43628 decimalPrecision : 0,
43630 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43632 allowNegative : true,
43634 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43638 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43640 minValue : Number.NEGATIVE_INFINITY,
43642 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43644 maxValue : Number.MAX_VALUE,
43646 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43648 minText : "The minimum value for this field is {0}",
43650 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43652 maxText : "The maximum value for this field is {0}",
43654 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43655 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43657 nanText : "{0} is not a valid number",
43659 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43663 * @cfg {String} defaults currency of the MoneyField
43664 * value should be in lkey
43666 defaultCurrency : false,
43668 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43670 thousandsDelimiter : false,
43672 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43683 getAutoCreate : function()
43685 var align = this.labelAlign || this.parentLabelAlign();
43697 cls : 'form-control roo-money-amount-input',
43698 autocomplete: 'new-password'
43701 var hiddenInput = {
43705 cls: 'hidden-number-input'
43708 if(this.max_length) {
43709 input.maxlength = this.max_length;
43713 hiddenInput.name = this.name;
43716 if (this.disabled) {
43717 input.disabled = true;
43720 var clg = 12 - this.inputlg;
43721 var cmd = 12 - this.inputmd;
43722 var csm = 12 - this.inputsm;
43723 var cxs = 12 - this.inputxs;
43727 cls : 'row roo-money-field',
43731 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43735 cls: 'roo-select2-container input-group',
43739 cls : 'form-control roo-money-currency-input',
43740 autocomplete: 'new-password',
43742 name : this.currencyName
43746 cls : 'input-group-addon',
43760 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43764 cls: this.hasFeedback ? 'has-feedback' : '',
43775 if (this.fieldLabel.length) {
43778 tooltip: 'This field is required'
43784 cls: 'control-label',
43790 html: this.fieldLabel
43793 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43799 if(this.indicatorpos == 'right') {
43800 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43807 if(align == 'left') {
43815 if(this.labelWidth > 12){
43816 label.style = "width: " + this.labelWidth + 'px';
43818 if(this.labelWidth < 13 && this.labelmd == 0){
43819 this.labelmd = this.labelWidth;
43821 if(this.labellg > 0){
43822 label.cls += ' col-lg-' + this.labellg;
43823 input.cls += ' col-lg-' + (12 - this.labellg);
43825 if(this.labelmd > 0){
43826 label.cls += ' col-md-' + this.labelmd;
43827 container.cls += ' col-md-' + (12 - this.labelmd);
43829 if(this.labelsm > 0){
43830 label.cls += ' col-sm-' + this.labelsm;
43831 container.cls += ' col-sm-' + (12 - this.labelsm);
43833 if(this.labelxs > 0){
43834 label.cls += ' col-xs-' + this.labelxs;
43835 container.cls += ' col-xs-' + (12 - this.labelxs);
43846 var settings = this;
43848 ['xs','sm','md','lg'].map(function(size){
43849 if (settings[size]) {
43850 cfg.cls += ' col-' + size + '-' + settings[size];
43857 initEvents : function()
43859 this.indicator = this.indicatorEl();
43861 this.initCurrencyEvent();
43863 this.initNumberEvent();
43866 initCurrencyEvent : function()
43869 throw "can not find store for combo";
43872 this.store = Roo.factory(this.store, Roo.data);
43873 this.store.parent = this;
43877 this.triggerEl = this.el.select('.input-group-addon', true).first();
43879 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43884 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43885 _this.list.setWidth(lw);
43888 this.list.on('mouseover', this.onViewOver, this);
43889 this.list.on('mousemove', this.onViewMove, this);
43890 this.list.on('scroll', this.onViewScroll, this);
43893 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43896 this.view = new Roo.View(this.list, this.tpl, {
43897 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43900 this.view.on('click', this.onViewClick, this);
43902 this.store.on('beforeload', this.onBeforeLoad, this);
43903 this.store.on('load', this.onLoad, this);
43904 this.store.on('loadexception', this.onLoadException, this);
43906 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43907 "up" : function(e){
43908 this.inKeyMode = true;
43912 "down" : function(e){
43913 if(!this.isExpanded()){
43914 this.onTriggerClick();
43916 this.inKeyMode = true;
43921 "enter" : function(e){
43924 if(this.fireEvent("specialkey", this, e)){
43925 this.onViewClick(false);
43931 "esc" : function(e){
43935 "tab" : function(e){
43938 if(this.fireEvent("specialkey", this, e)){
43939 this.onViewClick(false);
43947 doRelay : function(foo, bar, hname){
43948 if(hname == 'down' || this.scope.isExpanded()){
43949 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43957 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43961 initNumberEvent : function(e)
43963 this.inputEl().on("keydown" , this.fireKey, this);
43964 this.inputEl().on("focus", this.onFocus, this);
43965 this.inputEl().on("blur", this.onBlur, this);
43967 this.inputEl().relayEvent('keyup', this);
43969 if(this.indicator){
43970 this.indicator.addClass('invisible');
43973 this.originalValue = this.getValue();
43975 if(this.validationEvent == 'keyup'){
43976 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43977 this.inputEl().on('keyup', this.filterValidation, this);
43979 else if(this.validationEvent !== false){
43980 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43983 if(this.selectOnFocus){
43984 this.on("focus", this.preFocus, this);
43987 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43988 this.inputEl().on("keypress", this.filterKeys, this);
43990 this.inputEl().relayEvent('keypress', this);
43993 var allowed = "0123456789";
43995 if(this.allowDecimals){
43996 allowed += this.decimalSeparator;
43999 if(this.allowNegative){
44003 if(this.thousandsDelimiter) {
44007 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44009 var keyPress = function(e){
44011 var k = e.getKey();
44013 var c = e.getCharCode();
44016 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44017 allowed.indexOf(String.fromCharCode(c)) === -1
44023 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44027 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44032 this.inputEl().on("keypress", keyPress, this);
44036 onTriggerClick : function(e)
44043 this.loadNext = false;
44045 if(this.isExpanded()){
44050 this.hasFocus = true;
44052 if(this.triggerAction == 'all') {
44053 this.doQuery(this.allQuery, true);
44057 this.doQuery(this.getRawValue());
44060 getCurrency : function()
44062 var v = this.currencyEl().getValue();
44067 restrictHeight : function()
44069 this.list.alignTo(this.currencyEl(), this.listAlign);
44070 this.list.alignTo(this.currencyEl(), this.listAlign);
44073 onViewClick : function(view, doFocus, el, e)
44075 var index = this.view.getSelectedIndexes()[0];
44077 var r = this.store.getAt(index);
44080 this.onSelect(r, index);
44084 onSelect : function(record, index){
44086 if(this.fireEvent('beforeselect', this, record, index) !== false){
44088 this.setFromCurrencyData(index > -1 ? record.data : false);
44092 this.fireEvent('select', this, record, index);
44096 setFromCurrencyData : function(o)
44100 this.lastCurrency = o;
44102 if (this.currencyField) {
44103 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44105 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44108 this.lastSelectionText = currency;
44110 //setting default currency
44111 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44112 this.setCurrency(this.defaultCurrency);
44116 this.setCurrency(currency);
44119 setFromData : function(o)
44123 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44125 this.setFromCurrencyData(c);
44130 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44132 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44135 this.setValue(value);
44139 setCurrency : function(v)
44141 this.currencyValue = v;
44144 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44149 setValue : function(v)
44151 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44157 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44159 this.inputEl().dom.value = (v == '') ? '' :
44160 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44162 if(!this.allowZero && v === '0') {
44163 this.hiddenEl().dom.value = '';
44164 this.inputEl().dom.value = '';
44171 getRawValue : function()
44173 var v = this.inputEl().getValue();
44178 getValue : function()
44180 return this.fixPrecision(this.parseValue(this.getRawValue()));
44183 parseValue : function(value)
44185 if(this.thousandsDelimiter) {
44187 r = new RegExp(",", "g");
44188 value = value.replace(r, "");
44191 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44192 return isNaN(value) ? '' : value;
44196 fixPrecision : function(value)
44198 if(this.thousandsDelimiter) {
44200 r = new RegExp(",", "g");
44201 value = value.replace(r, "");
44204 var nan = isNaN(value);
44206 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44207 return nan ? '' : value;
44209 return parseFloat(value).toFixed(this.decimalPrecision);
44212 decimalPrecisionFcn : function(v)
44214 return Math.floor(v);
44217 validateValue : function(value)
44219 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44223 var num = this.parseValue(value);
44226 this.markInvalid(String.format(this.nanText, value));
44230 if(num < this.minValue){
44231 this.markInvalid(String.format(this.minText, this.minValue));
44235 if(num > this.maxValue){
44236 this.markInvalid(String.format(this.maxText, this.maxValue));
44243 validate : function()
44245 if(this.disabled || this.allowBlank){
44250 var currency = this.getCurrency();
44252 if(this.validateValue(this.getRawValue()) && currency.length){
44257 this.markInvalid();
44261 getName: function()
44266 beforeBlur : function()
44272 var v = this.parseValue(this.getRawValue());
44279 onBlur : function()
44283 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44284 //this.el.removeClass(this.focusClass);
44287 this.hasFocus = false;
44289 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44293 var v = this.getValue();
44295 if(String(v) !== String(this.startValue)){
44296 this.fireEvent('change', this, v, this.startValue);
44299 this.fireEvent("blur", this);
44302 inputEl : function()
44304 return this.el.select('.roo-money-amount-input', true).first();
44307 currencyEl : function()
44309 return this.el.select('.roo-money-currency-input', true).first();
44312 hiddenEl : function()
44314 return this.el.select('input.hidden-number-input',true).first();
44318 * @class Roo.bootstrap.BezierSignature
44319 * @extends Roo.bootstrap.Component
44320 * Bootstrap BezierSignature class
44321 * This script refer to:
44322 * Title: Signature Pad
44324 * Availability: https://github.com/szimek/signature_pad
44327 * Create a new BezierSignature
44328 * @param {Object} config The config object
44331 Roo.bootstrap.BezierSignature = function(config){
44332 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44338 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44345 mouse_btn_down: true,
44348 * @cfg {int} canvas height
44350 canvas_height: '200px',
44353 * @cfg {float|function} Radius of a single dot.
44358 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44363 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44368 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44373 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44378 * @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.
44380 bg_color: 'rgba(0, 0, 0, 0)',
44383 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44385 dot_color: 'black',
44388 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44390 velocity_filter_weight: 0.7,
44393 * @cfg {function} Callback when stroke begin.
44398 * @cfg {function} Callback when stroke end.
44402 getAutoCreate : function()
44404 var cls = 'roo-signature column';
44407 cls += ' ' + this.cls;
44417 for(var i = 0; i < col_sizes.length; i++) {
44418 if(this[col_sizes[i]]) {
44419 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44429 cls: 'roo-signature-body',
44433 cls: 'roo-signature-body-canvas',
44434 height: this.canvas_height,
44435 width: this.canvas_width
44442 style: 'display: none'
44450 initEvents: function()
44452 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44454 var canvas = this.canvasEl();
44456 // mouse && touch event swapping...
44457 canvas.dom.style.touchAction = 'none';
44458 canvas.dom.style.msTouchAction = 'none';
44460 this.mouse_btn_down = false;
44461 canvas.on('mousedown', this._handleMouseDown, this);
44462 canvas.on('mousemove', this._handleMouseMove, this);
44463 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44465 if (window.PointerEvent) {
44466 canvas.on('pointerdown', this._handleMouseDown, this);
44467 canvas.on('pointermove', this._handleMouseMove, this);
44468 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44471 if ('ontouchstart' in window) {
44472 canvas.on('touchstart', this._handleTouchStart, this);
44473 canvas.on('touchmove', this._handleTouchMove, this);
44474 canvas.on('touchend', this._handleTouchEnd, this);
44477 Roo.EventManager.onWindowResize(this.resize, this, true);
44479 // file input event
44480 this.fileEl().on('change', this.uploadImage, this);
44487 resize: function(){
44489 var canvas = this.canvasEl().dom;
44490 var ctx = this.canvasElCtx();
44491 var img_data = false;
44493 if(canvas.width > 0) {
44494 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44496 // setting canvas width will clean img data
44499 var style = window.getComputedStyle ?
44500 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44502 var padding_left = parseInt(style.paddingLeft) || 0;
44503 var padding_right = parseInt(style.paddingRight) || 0;
44505 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44508 ctx.putImageData(img_data, 0, 0);
44512 _handleMouseDown: function(e)
44514 if (e.browserEvent.which === 1) {
44515 this.mouse_btn_down = true;
44516 this.strokeBegin(e);
44520 _handleMouseMove: function (e)
44522 if (this.mouse_btn_down) {
44523 this.strokeMoveUpdate(e);
44527 _handleMouseUp: function (e)
44529 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44530 this.mouse_btn_down = false;
44535 _handleTouchStart: function (e) {
44537 e.preventDefault();
44538 if (e.browserEvent.targetTouches.length === 1) {
44539 // var touch = e.browserEvent.changedTouches[0];
44540 // this.strokeBegin(touch);
44542 this.strokeBegin(e); // assume e catching the correct xy...
44546 _handleTouchMove: function (e) {
44547 e.preventDefault();
44548 // var touch = event.targetTouches[0];
44549 // _this._strokeMoveUpdate(touch);
44550 this.strokeMoveUpdate(e);
44553 _handleTouchEnd: function (e) {
44554 var wasCanvasTouched = e.target === this.canvasEl().dom;
44555 if (wasCanvasTouched) {
44556 e.preventDefault();
44557 // var touch = event.changedTouches[0];
44558 // _this._strokeEnd(touch);
44563 reset: function () {
44564 this._lastPoints = [];
44565 this._lastVelocity = 0;
44566 this._lastWidth = (this.min_width + this.max_width) / 2;
44567 this.canvasElCtx().fillStyle = this.dot_color;
44570 strokeMoveUpdate: function(e)
44572 this.strokeUpdate(e);
44574 if (this.throttle) {
44575 this.throttleStroke(this.strokeUpdate, this.throttle);
44578 this.strokeUpdate(e);
44582 strokeBegin: function(e)
44584 var newPointGroup = {
44585 color: this.dot_color,
44589 if (typeof this.onBegin === 'function') {
44593 this.curve_data.push(newPointGroup);
44595 this.strokeUpdate(e);
44598 strokeUpdate: function(e)
44600 var rect = this.canvasEl().dom.getBoundingClientRect();
44601 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44602 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44603 var lastPoints = lastPointGroup.points;
44604 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44605 var isLastPointTooClose = lastPoint
44606 ? point.distanceTo(lastPoint) <= this.min_distance
44608 var color = lastPointGroup.color;
44609 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44610 var curve = this.addPoint(point);
44612 this.drawDot({color: color, point: point});
44615 this.drawCurve({color: color, curve: curve});
44625 strokeEnd: function(e)
44627 this.strokeUpdate(e);
44628 if (typeof this.onEnd === 'function') {
44633 addPoint: function (point) {
44634 var _lastPoints = this._lastPoints;
44635 _lastPoints.push(point);
44636 if (_lastPoints.length > 2) {
44637 if (_lastPoints.length === 3) {
44638 _lastPoints.unshift(_lastPoints[0]);
44640 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44641 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44642 _lastPoints.shift();
44648 calculateCurveWidths: function (startPoint, endPoint) {
44649 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44650 (1 - this.velocity_filter_weight) * this._lastVelocity;
44652 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44655 start: this._lastWidth
44658 this._lastVelocity = velocity;
44659 this._lastWidth = newWidth;
44663 drawDot: function (_a) {
44664 var color = _a.color, point = _a.point;
44665 var ctx = this.canvasElCtx();
44666 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44668 this.drawCurveSegment(point.x, point.y, width);
44670 ctx.fillStyle = color;
44674 drawCurve: function (_a) {
44675 var color = _a.color, curve = _a.curve;
44676 var ctx = this.canvasElCtx();
44677 var widthDelta = curve.endWidth - curve.startWidth;
44678 var drawSteps = Math.floor(curve.length()) * 2;
44680 ctx.fillStyle = color;
44681 for (var i = 0; i < drawSteps; i += 1) {
44682 var t = i / drawSteps;
44688 var x = uuu * curve.startPoint.x;
44689 x += 3 * uu * t * curve.control1.x;
44690 x += 3 * u * tt * curve.control2.x;
44691 x += ttt * curve.endPoint.x;
44692 var y = uuu * curve.startPoint.y;
44693 y += 3 * uu * t * curve.control1.y;
44694 y += 3 * u * tt * curve.control2.y;
44695 y += ttt * curve.endPoint.y;
44696 var width = curve.startWidth + ttt * widthDelta;
44697 this.drawCurveSegment(x, y, width);
44703 drawCurveSegment: function (x, y, width) {
44704 var ctx = this.canvasElCtx();
44706 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44707 this.is_empty = false;
44712 var ctx = this.canvasElCtx();
44713 var canvas = this.canvasEl().dom;
44714 ctx.fillStyle = this.bg_color;
44715 ctx.clearRect(0, 0, canvas.width, canvas.height);
44716 ctx.fillRect(0, 0, canvas.width, canvas.height);
44717 this.curve_data = [];
44719 this.is_empty = true;
44724 return this.el.select('input',true).first();
44727 canvasEl: function()
44729 return this.el.select('canvas',true).first();
44732 canvasElCtx: function()
44734 return this.el.select('canvas',true).first().dom.getContext('2d');
44737 getImage: function(type)
44739 if(this.is_empty) {
44744 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44747 drawFromImage: function(img_src)
44749 var img = new Image();
44751 img.onload = function(){
44752 this.canvasElCtx().drawImage(img, 0, 0);
44757 this.is_empty = false;
44760 selectImage: function()
44762 this.fileEl().dom.click();
44765 uploadImage: function(e)
44767 var reader = new FileReader();
44769 reader.onload = function(e){
44770 var img = new Image();
44771 img.onload = function(){
44773 this.canvasElCtx().drawImage(img, 0, 0);
44775 img.src = e.target.result;
44778 reader.readAsDataURL(e.target.files[0]);
44781 // Bezier Point Constructor
44782 Point: (function () {
44783 function Point(x, y, time) {
44786 this.time = time || Date.now();
44788 Point.prototype.distanceTo = function (start) {
44789 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44791 Point.prototype.equals = function (other) {
44792 return this.x === other.x && this.y === other.y && this.time === other.time;
44794 Point.prototype.velocityFrom = function (start) {
44795 return this.time !== start.time
44796 ? this.distanceTo(start) / (this.time - start.time)
44803 // Bezier Constructor
44804 Bezier: (function () {
44805 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44806 this.startPoint = startPoint;
44807 this.control2 = control2;
44808 this.control1 = control1;
44809 this.endPoint = endPoint;
44810 this.startWidth = startWidth;
44811 this.endWidth = endWidth;
44813 Bezier.fromPoints = function (points, widths, scope) {
44814 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44815 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44816 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44818 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44819 var dx1 = s1.x - s2.x;
44820 var dy1 = s1.y - s2.y;
44821 var dx2 = s2.x - s3.x;
44822 var dy2 = s2.y - s3.y;
44823 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44824 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44825 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44826 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44827 var dxm = m1.x - m2.x;
44828 var dym = m1.y - m2.y;
44829 var k = l2 / (l1 + l2);
44830 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44831 var tx = s2.x - cm.x;
44832 var ty = s2.y - cm.y;
44834 c1: new scope.Point(m1.x + tx, m1.y + ty),
44835 c2: new scope.Point(m2.x + tx, m2.y + ty)
44838 Bezier.prototype.length = function () {
44843 for (var i = 0; i <= steps; i += 1) {
44845 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44846 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44848 var xdiff = cx - px;
44849 var ydiff = cy - py;
44850 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44857 Bezier.prototype.point = function (t, start, c1, c2, end) {
44858 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44859 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44860 + (3.0 * c2 * (1.0 - t) * t * t)
44861 + (end * t * t * t);
44866 throttleStroke: function(fn, wait) {
44867 if (wait === void 0) { wait = 250; }
44869 var timeout = null;
44873 var later = function () {
44874 previous = Date.now();
44876 result = fn.apply(storedContext, storedArgs);
44878 storedContext = null;
44882 return function wrapper() {
44884 for (var _i = 0; _i < arguments.length; _i++) {
44885 args[_i] = arguments[_i];
44887 var now = Date.now();
44888 var remaining = wait - (now - previous);
44889 storedContext = this;
44891 if (remaining <= 0 || remaining > wait) {
44893 clearTimeout(timeout);
44897 result = fn.apply(storedContext, storedArgs);
44899 storedContext = null;
44903 else if (!timeout) {
44904 timeout = window.setTimeout(later, remaining);