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 // this might end up displaying HTML?
8885 tooltip : (typeof(value) === 'object') ? '' : value,
8886 cls : rowcfg.rowClass + ' x-col-' + i,
8888 html: (typeof(value) === 'object') ? '' : value
8895 if(typeof(config.colspan) != 'undefined'){
8896 td.colspan = config.colspan;
8899 if(typeof(config.hidden) != 'undefined' && config.hidden){
8900 td.style += ' display:none;';
8903 if(typeof(config.align) != 'undefined' && config.align.length){
8904 td.style += ' text-align:' + config.align + ';';
8906 if(typeof(config.valign) != 'undefined' && config.valign.length){
8907 td.style += ' vertical-align:' + config.valign + ';';
8910 if(typeof(config.width) != 'undefined'){
8911 td.style += ' width:' + config.width + 'px;';
8914 if(typeof(config.cursor) != 'undefined'){
8915 td.style += ' cursor:' + config.cursor + ';';
8918 if(typeof(config.cls) != 'undefined'){
8919 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8922 ['xs','sm','md','lg'].map(function(size){
8924 if(typeof(config[size]) == 'undefined'){
8930 if (!config[size]) { // 0 = hidden
8931 // BS 4 '0' is treated as hide that column and below.
8932 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8936 td.cls += ' col-' + size + '-' + config[size] + (
8937 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8947 row.cellObjects = cellObjects;
8955 onBeforeLoad : function()
8964 this.el.select('tbody', true).first().dom.innerHTML = '';
8967 * Show or hide a row.
8968 * @param {Number} rowIndex to show or hide
8969 * @param {Boolean} state hide
8971 setRowVisibility : function(rowIndex, state)
8973 var bt = this.mainBody.dom;
8975 var rows = this.el.select('tbody > tr', true).elements;
8977 if(typeof(rows[rowIndex]) == 'undefined'){
8980 rows[rowIndex].dom.style.display = state ? '' : 'none';
8984 getSelectionModel : function(){
8986 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8988 return this.selModel;
8991 * Render the Roo.bootstrap object from renderder
8993 renderCellObject : function(r)
8997 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8999 var t = r.cfg.render(r.container);
9002 Roo.each(r.cfg.cn, function(c){
9004 container: t.getChildContainer(),
9007 _this.renderCellObject(child);
9012 getRowIndex : function(row)
9016 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9027 * Returns the grid's underlying element = used by panel.Grid
9028 * @return {Element} The element
9030 getGridEl : function(){
9034 * Forces a resize - used by panel.Grid
9035 * @return {Element} The element
9037 autoSize : function()
9039 //var ctr = Roo.get(this.container.dom.parentElement);
9040 var ctr = Roo.get(this.el.dom);
9042 var thd = this.getGridEl().select('thead',true).first();
9043 var tbd = this.getGridEl().select('tbody', true).first();
9044 var tfd = this.getGridEl().select('tfoot', true).first();
9046 var cw = ctr.getWidth();
9047 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9051 tbd.setWidth(ctr.getWidth());
9052 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9053 // this needs fixing for various usage - currently only hydra job advers I think..
9055 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9057 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9060 cw = Math.max(cw, this.totalWidth);
9061 this.getGridEl().select('tbody tr',true).setWidth(cw);
9063 // resize 'expandable coloumn?
9065 return; // we doe not have a view in this design..
9068 onBodyScroll: function()
9070 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9072 this.mainHead.setStyle({
9073 'position' : 'relative',
9074 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9080 var scrollHeight = this.mainBody.dom.scrollHeight;
9082 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9084 var height = this.mainBody.getHeight();
9086 if(scrollHeight - height == scrollTop) {
9088 var total = this.ds.getTotalCount();
9090 if(this.footer.cursor + this.footer.pageSize < total){
9092 this.footer.ds.load({
9094 start : this.footer.cursor + this.footer.pageSize,
9095 limit : this.footer.pageSize
9105 onHeaderChange : function()
9107 var header = this.renderHeader();
9108 var table = this.el.select('table', true).first();
9110 this.mainHead.remove();
9111 this.mainHead = table.createChild(header, this.mainBody, false);
9113 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9114 e.on('click', this.sort, this);
9120 onHiddenChange : function(colModel, colIndex, hidden)
9122 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9123 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9125 this.CSS.updateRule(thSelector, "display", "");
9126 this.CSS.updateRule(tdSelector, "display", "");
9129 this.CSS.updateRule(thSelector, "display", "none");
9130 this.CSS.updateRule(tdSelector, "display", "none");
9133 this.onHeaderChange();
9137 setColumnWidth: function(col_index, width)
9139 // width = "md-2 xs-2..."
9140 if(!this.colModel.config[col_index]) {
9144 var w = width.split(" ");
9146 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9148 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9151 for(var j = 0; j < w.length; j++) {
9157 var size_cls = w[j].split("-");
9159 if(!Number.isInteger(size_cls[1] * 1)) {
9163 if(!this.colModel.config[col_index][size_cls[0]]) {
9167 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9171 h_row[0].classList.replace(
9172 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9173 "col-"+size_cls[0]+"-"+size_cls[1]
9176 for(var i = 0; i < rows.length; i++) {
9178 var size_cls = w[j].split("-");
9180 if(!Number.isInteger(size_cls[1] * 1)) {
9184 if(!this.colModel.config[col_index][size_cls[0]]) {
9188 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9192 rows[i].classList.replace(
9193 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9194 "col-"+size_cls[0]+"-"+size_cls[1]
9198 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9213 * @class Roo.bootstrap.TableCell
9214 * @extends Roo.bootstrap.Component
9215 * Bootstrap TableCell class
9216 * @cfg {String} html cell contain text
9217 * @cfg {String} cls cell class
9218 * @cfg {String} tag cell tag (td|th) default td
9219 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9220 * @cfg {String} align Aligns the content in a cell
9221 * @cfg {String} axis Categorizes cells
9222 * @cfg {String} bgcolor Specifies the background color of a cell
9223 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9224 * @cfg {Number} colspan Specifies the number of columns a cell should span
9225 * @cfg {String} headers Specifies one or more header cells a cell is related to
9226 * @cfg {Number} height Sets the height of a cell
9227 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9228 * @cfg {Number} rowspan Sets the number of rows a cell should span
9229 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9230 * @cfg {String} valign Vertical aligns the content in a cell
9231 * @cfg {Number} width Specifies the width of a cell
9234 * Create a new TableCell
9235 * @param {Object} config The config object
9238 Roo.bootstrap.TableCell = function(config){
9239 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9242 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9262 getAutoCreate : function(){
9263 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9283 cfg.align=this.align
9289 cfg.bgcolor=this.bgcolor
9292 cfg.charoff=this.charoff
9295 cfg.colspan=this.colspan
9298 cfg.headers=this.headers
9301 cfg.height=this.height
9304 cfg.nowrap=this.nowrap
9307 cfg.rowspan=this.rowspan
9310 cfg.scope=this.scope
9313 cfg.valign=this.valign
9316 cfg.width=this.width
9335 * @class Roo.bootstrap.TableRow
9336 * @extends Roo.bootstrap.Component
9337 * Bootstrap TableRow class
9338 * @cfg {String} cls row class
9339 * @cfg {String} align Aligns the content in a table row
9340 * @cfg {String} bgcolor Specifies a background color for a table row
9341 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9342 * @cfg {String} valign Vertical aligns the content in a table row
9345 * Create a new TableRow
9346 * @param {Object} config The config object
9349 Roo.bootstrap.TableRow = function(config){
9350 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9353 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9361 getAutoCreate : function(){
9362 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9372 cfg.align = this.align;
9375 cfg.bgcolor = this.bgcolor;
9378 cfg.charoff = this.charoff;
9381 cfg.valign = this.valign;
9399 * @class Roo.bootstrap.TableBody
9400 * @extends Roo.bootstrap.Component
9401 * Bootstrap TableBody class
9402 * @cfg {String} cls element class
9403 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9404 * @cfg {String} align Aligns the content inside the element
9405 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9406 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9409 * Create a new TableBody
9410 * @param {Object} config The config object
9413 Roo.bootstrap.TableBody = function(config){
9414 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9417 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9425 getAutoCreate : function(){
9426 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9440 cfg.align = this.align;
9443 cfg.charoff = this.charoff;
9446 cfg.valign = this.valign;
9453 // initEvents : function()
9460 // this.store = Roo.factory(this.store, Roo.data);
9461 // this.store.on('load', this.onLoad, this);
9463 // this.store.load();
9467 // onLoad: function ()
9469 // this.fireEvent('load', this);
9479 * Ext JS Library 1.1.1
9480 * Copyright(c) 2006-2007, Ext JS, LLC.
9482 * Originally Released Under LGPL - original licence link has changed is not relivant.
9485 * <script type="text/javascript">
9488 // as we use this in bootstrap.
9489 Roo.namespace('Roo.form');
9491 * @class Roo.form.Action
9492 * Internal Class used to handle form actions
9494 * @param {Roo.form.BasicForm} el The form element or its id
9495 * @param {Object} config Configuration options
9500 // define the action interface
9501 Roo.form.Action = function(form, options){
9503 this.options = options || {};
9506 * Client Validation Failed
9509 Roo.form.Action.CLIENT_INVALID = 'client';
9511 * Server Validation Failed
9514 Roo.form.Action.SERVER_INVALID = 'server';
9516 * Connect to Server Failed
9519 Roo.form.Action.CONNECT_FAILURE = 'connect';
9521 * Reading Data from Server Failed
9524 Roo.form.Action.LOAD_FAILURE = 'load';
9526 Roo.form.Action.prototype = {
9528 failureType : undefined,
9529 response : undefined,
9533 run : function(options){
9538 success : function(response){
9543 handleResponse : function(response){
9547 // default connection failure
9548 failure : function(response){
9550 this.response = response;
9551 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9552 this.form.afterAction(this, false);
9555 processResponse : function(response){
9556 this.response = response;
9557 if(!response.responseText){
9560 this.result = this.handleResponse(response);
9564 // utility functions used internally
9565 getUrl : function(appendParams){
9566 var url = this.options.url || this.form.url || this.form.el.dom.action;
9568 var p = this.getParams();
9570 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9576 getMethod : function(){
9577 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9580 getParams : function(){
9581 var bp = this.form.baseParams;
9582 var p = this.options.params;
9584 if(typeof p == "object"){
9585 p = Roo.urlEncode(Roo.applyIf(p, bp));
9586 }else if(typeof p == 'string' && bp){
9587 p += '&' + Roo.urlEncode(bp);
9590 p = Roo.urlEncode(bp);
9595 createCallback : function(){
9597 success: this.success,
9598 failure: this.failure,
9600 timeout: (this.form.timeout*1000),
9601 upload: this.form.fileUpload ? this.success : undefined
9606 Roo.form.Action.Submit = function(form, options){
9607 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9610 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9613 haveProgress : false,
9614 uploadComplete : false,
9616 // uploadProgress indicator.
9617 uploadProgress : function()
9619 if (!this.form.progressUrl) {
9623 if (!this.haveProgress) {
9624 Roo.MessageBox.progress("Uploading", "Uploading");
9626 if (this.uploadComplete) {
9627 Roo.MessageBox.hide();
9631 this.haveProgress = true;
9633 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9635 var c = new Roo.data.Connection();
9637 url : this.form.progressUrl,
9642 success : function(req){
9643 //console.log(data);
9647 rdata = Roo.decode(req.responseText)
9649 Roo.log("Invalid data from server..");
9653 if (!rdata || !rdata.success) {
9655 Roo.MessageBox.alert(Roo.encode(rdata));
9658 var data = rdata.data;
9660 if (this.uploadComplete) {
9661 Roo.MessageBox.hide();
9666 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9667 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9670 this.uploadProgress.defer(2000,this);
9673 failure: function(data) {
9674 Roo.log('progress url failed ');
9685 // run get Values on the form, so it syncs any secondary forms.
9686 this.form.getValues();
9688 var o = this.options;
9689 var method = this.getMethod();
9690 var isPost = method == 'POST';
9691 if(o.clientValidation === false || this.form.isValid()){
9693 if (this.form.progressUrl) {
9694 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9695 (new Date() * 1) + '' + Math.random());
9700 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9701 form:this.form.el.dom,
9702 url:this.getUrl(!isPost),
9704 params:isPost ? this.getParams() : null,
9705 isUpload: this.form.fileUpload,
9706 formData : this.form.formData
9709 this.uploadProgress();
9711 }else if (o.clientValidation !== false){ // client validation failed
9712 this.failureType = Roo.form.Action.CLIENT_INVALID;
9713 this.form.afterAction(this, false);
9717 success : function(response)
9719 this.uploadComplete= true;
9720 if (this.haveProgress) {
9721 Roo.MessageBox.hide();
9725 var result = this.processResponse(response);
9726 if(result === true || result.success){
9727 this.form.afterAction(this, true);
9731 this.form.markInvalid(result.errors);
9732 this.failureType = Roo.form.Action.SERVER_INVALID;
9734 this.form.afterAction(this, false);
9736 failure : function(response)
9738 this.uploadComplete= true;
9739 if (this.haveProgress) {
9740 Roo.MessageBox.hide();
9743 this.response = response;
9744 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9745 this.form.afterAction(this, false);
9748 handleResponse : function(response){
9749 if(this.form.errorReader){
9750 var rs = this.form.errorReader.read(response);
9753 for(var i = 0, len = rs.records.length; i < len; i++) {
9754 var r = rs.records[i];
9758 if(errors.length < 1){
9762 success : rs.success,
9768 ret = Roo.decode(response.responseText);
9772 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9782 Roo.form.Action.Load = function(form, options){
9783 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9784 this.reader = this.form.reader;
9787 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9792 Roo.Ajax.request(Roo.apply(
9793 this.createCallback(), {
9794 method:this.getMethod(),
9795 url:this.getUrl(false),
9796 params:this.getParams()
9800 success : function(response){
9802 var result = this.processResponse(response);
9803 if(result === true || !result.success || !result.data){
9804 this.failureType = Roo.form.Action.LOAD_FAILURE;
9805 this.form.afterAction(this, false);
9808 this.form.clearInvalid();
9809 this.form.setValues(result.data);
9810 this.form.afterAction(this, true);
9813 handleResponse : function(response){
9814 if(this.form.reader){
9815 var rs = this.form.reader.read(response);
9816 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9818 success : rs.success,
9822 return Roo.decode(response.responseText);
9826 Roo.form.Action.ACTION_TYPES = {
9827 'load' : Roo.form.Action.Load,
9828 'submit' : Roo.form.Action.Submit
9837 * @class Roo.bootstrap.Form
9838 * @extends Roo.bootstrap.Component
9839 * Bootstrap Form class
9840 * @cfg {String} method GET | POST (default POST)
9841 * @cfg {String} labelAlign top | left (default top)
9842 * @cfg {String} align left | right - for navbars
9843 * @cfg {Boolean} loadMask load mask when submit (default true)
9848 * @param {Object} config The config object
9852 Roo.bootstrap.Form = function(config){
9854 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9856 Roo.bootstrap.Form.popover.apply();
9860 * @event clientvalidation
9861 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9862 * @param {Form} this
9863 * @param {Boolean} valid true if the form has passed client-side validation
9865 clientvalidation: true,
9867 * @event beforeaction
9868 * Fires before any action is performed. Return false to cancel the action.
9869 * @param {Form} this
9870 * @param {Action} action The action to be performed
9874 * @event actionfailed
9875 * Fires when an action fails.
9876 * @param {Form} this
9877 * @param {Action} action The action that failed
9879 actionfailed : true,
9881 * @event actioncomplete
9882 * Fires when an action is completed.
9883 * @param {Form} this
9884 * @param {Action} action The action that completed
9886 actioncomplete : true
9890 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9893 * @cfg {String} method
9894 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9899 * The URL to use for form actions if one isn't supplied in the action options.
9902 * @cfg {Boolean} fileUpload
9903 * Set to true if this form is a file upload.
9907 * @cfg {Object} baseParams
9908 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9912 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9916 * @cfg {Sting} align (left|right) for navbar forms
9921 activeAction : null,
9924 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9925 * element by passing it or its id or mask the form itself by passing in true.
9928 waitMsgTarget : false,
9933 * @cfg {Boolean} errorMask (true|false) default false
9938 * @cfg {Number} maskOffset Default 100
9943 * @cfg {Boolean} maskBody
9947 getAutoCreate : function(){
9951 method : this.method || 'POST',
9952 id : this.id || Roo.id(),
9955 if (this.parent().xtype.match(/^Nav/)) {
9956 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9960 if (this.labelAlign == 'left' ) {
9961 cfg.cls += ' form-horizontal';
9967 initEvents : function()
9969 this.el.on('submit', this.onSubmit, this);
9970 // this was added as random key presses on the form where triggering form submit.
9971 this.el.on('keypress', function(e) {
9972 if (e.getCharCode() != 13) {
9975 // we might need to allow it for textareas.. and some other items.
9976 // check e.getTarget().
9978 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9982 Roo.log("keypress blocked");
9990 onSubmit : function(e){
9995 * Returns true if client-side validation on the form is successful.
9998 isValid : function(){
9999 var items = this.getItems();
10001 var target = false;
10003 items.each(function(f){
10009 Roo.log('invalid field: ' + f.name);
10013 if(!target && f.el.isVisible(true)){
10019 if(this.errorMask && !valid){
10020 Roo.bootstrap.Form.popover.mask(this, target);
10027 * Returns true if any fields in this form have changed since their original load.
10030 isDirty : function(){
10032 var items = this.getItems();
10033 items.each(function(f){
10043 * Performs a predefined action (submit or load) or custom actions you define on this form.
10044 * @param {String} actionName The name of the action type
10045 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10046 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10047 * accept other config options):
10049 Property Type Description
10050 ---------------- --------------- ----------------------------------------------------------------------------------
10051 url String The url for the action (defaults to the form's url)
10052 method String The form method to use (defaults to the form's method, or POST if not defined)
10053 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10054 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10055 validate the form on the client (defaults to false)
10057 * @return {BasicForm} this
10059 doAction : function(action, options){
10060 if(typeof action == 'string'){
10061 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10063 if(this.fireEvent('beforeaction', this, action) !== false){
10064 this.beforeAction(action);
10065 action.run.defer(100, action);
10071 beforeAction : function(action){
10072 var o = action.options;
10077 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10079 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10082 // not really supported yet.. ??
10084 //if(this.waitMsgTarget === true){
10085 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10086 //}else if(this.waitMsgTarget){
10087 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10088 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10090 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10096 afterAction : function(action, success){
10097 this.activeAction = null;
10098 var o = action.options;
10103 Roo.get(document.body).unmask();
10109 //if(this.waitMsgTarget === true){
10110 // this.el.unmask();
10111 //}else if(this.waitMsgTarget){
10112 // this.waitMsgTarget.unmask();
10114 // Roo.MessageBox.updateProgress(1);
10115 // Roo.MessageBox.hide();
10122 Roo.callback(o.success, o.scope, [this, action]);
10123 this.fireEvent('actioncomplete', this, action);
10127 // failure condition..
10128 // we have a scenario where updates need confirming.
10129 // eg. if a locking scenario exists..
10130 // we look for { errors : { needs_confirm : true }} in the response.
10132 (typeof(action.result) != 'undefined') &&
10133 (typeof(action.result.errors) != 'undefined') &&
10134 (typeof(action.result.errors.needs_confirm) != 'undefined')
10137 Roo.log("not supported yet");
10140 Roo.MessageBox.confirm(
10141 "Change requires confirmation",
10142 action.result.errorMsg,
10147 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10157 Roo.callback(o.failure, o.scope, [this, action]);
10158 // show an error message if no failed handler is set..
10159 if (!this.hasListener('actionfailed')) {
10160 Roo.log("need to add dialog support");
10162 Roo.MessageBox.alert("Error",
10163 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10164 action.result.errorMsg :
10165 "Saving Failed, please check your entries or try again"
10170 this.fireEvent('actionfailed', this, action);
10175 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10176 * @param {String} id The value to search for
10179 findField : function(id){
10180 var items = this.getItems();
10181 var field = items.get(id);
10183 items.each(function(f){
10184 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10191 return field || null;
10194 * Mark fields in this form invalid in bulk.
10195 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10196 * @return {BasicForm} this
10198 markInvalid : function(errors){
10199 if(errors instanceof Array){
10200 for(var i = 0, len = errors.length; i < len; i++){
10201 var fieldError = errors[i];
10202 var f = this.findField(fieldError.id);
10204 f.markInvalid(fieldError.msg);
10210 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10211 field.markInvalid(errors[id]);
10215 //Roo.each(this.childForms || [], function (f) {
10216 // f.markInvalid(errors);
10223 * Set values for fields in this form in bulk.
10224 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10225 * @return {BasicForm} this
10227 setValues : function(values){
10228 if(values instanceof Array){ // array of objects
10229 for(var i = 0, len = values.length; i < len; i++){
10231 var f = this.findField(v.id);
10233 f.setValue(v.value);
10234 if(this.trackResetOnLoad){
10235 f.originalValue = f.getValue();
10239 }else{ // object hash
10242 if(typeof values[id] != 'function' && (field = this.findField(id))){
10244 if (field.setFromData &&
10245 field.valueField &&
10246 field.displayField &&
10247 // combos' with local stores can
10248 // be queried via setValue()
10249 // to set their value..
10250 (field.store && !field.store.isLocal)
10254 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10255 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10256 field.setFromData(sd);
10258 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10260 field.setFromData(values);
10263 field.setValue(values[id]);
10267 if(this.trackResetOnLoad){
10268 field.originalValue = field.getValue();
10274 //Roo.each(this.childForms || [], function (f) {
10275 // f.setValues(values);
10282 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10283 * they are returned as an array.
10284 * @param {Boolean} asString
10287 getValues : function(asString){
10288 //if (this.childForms) {
10289 // copy values from the child forms
10290 // Roo.each(this.childForms, function (f) {
10291 // this.setValues(f.getValues());
10297 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10298 if(asString === true){
10301 return Roo.urlDecode(fs);
10305 * Returns the fields in this form as an object with key/value pairs.
10306 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10309 getFieldValues : function(with_hidden)
10311 var items = this.getItems();
10313 items.each(function(f){
10315 if (!f.getName()) {
10319 var v = f.getValue();
10321 if (f.inputType =='radio') {
10322 if (typeof(ret[f.getName()]) == 'undefined') {
10323 ret[f.getName()] = ''; // empty..
10326 if (!f.el.dom.checked) {
10330 v = f.el.dom.value;
10334 if(f.xtype == 'MoneyField'){
10335 ret[f.currencyName] = f.getCurrency();
10338 // not sure if this supported any more..
10339 if ((typeof(v) == 'object') && f.getRawValue) {
10340 v = f.getRawValue() ; // dates..
10342 // combo boxes where name != hiddenName...
10343 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10344 ret[f.name] = f.getRawValue();
10346 ret[f.getName()] = v;
10353 * Clears all invalid messages in this form.
10354 * @return {BasicForm} this
10356 clearInvalid : function(){
10357 var items = this.getItems();
10359 items.each(function(f){
10367 * Resets this form.
10368 * @return {BasicForm} this
10370 reset : function(){
10371 var items = this.getItems();
10372 items.each(function(f){
10376 Roo.each(this.childForms || [], function (f) {
10384 getItems : function()
10386 var r=new Roo.util.MixedCollection(false, function(o){
10387 return o.id || (o.id = Roo.id());
10389 var iter = function(el) {
10396 Roo.each(el.items,function(e) {
10405 hideFields : function(items)
10407 Roo.each(items, function(i){
10409 var f = this.findField(i);
10420 showFields : function(items)
10422 Roo.each(items, function(i){
10424 var f = this.findField(i);
10437 Roo.apply(Roo.bootstrap.Form, {
10453 intervalID : false,
10459 if(this.isApplied){
10464 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10465 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10466 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10467 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10470 this.maskEl.top.enableDisplayMode("block");
10471 this.maskEl.left.enableDisplayMode("block");
10472 this.maskEl.bottom.enableDisplayMode("block");
10473 this.maskEl.right.enableDisplayMode("block");
10475 this.toolTip = new Roo.bootstrap.Tooltip({
10476 cls : 'roo-form-error-popover',
10478 'left' : ['r-l', [-2,0], 'right'],
10479 'right' : ['l-r', [2,0], 'left'],
10480 'bottom' : ['tl-bl', [0,2], 'top'],
10481 'top' : [ 'bl-tl', [0,-2], 'bottom']
10485 this.toolTip.render(Roo.get(document.body));
10487 this.toolTip.el.enableDisplayMode("block");
10489 Roo.get(document.body).on('click', function(){
10493 Roo.get(document.body).on('touchstart', function(){
10497 this.isApplied = true
10500 mask : function(form, target)
10504 this.target = target;
10506 if(!this.form.errorMask || !target.el){
10510 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10512 Roo.log(scrollable);
10514 var ot = this.target.el.calcOffsetsTo(scrollable);
10516 var scrollTo = ot[1] - this.form.maskOffset;
10518 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10520 scrollable.scrollTo('top', scrollTo);
10522 var box = this.target.el.getBox();
10524 var zIndex = Roo.bootstrap.Modal.zIndex++;
10527 this.maskEl.top.setStyle('position', 'absolute');
10528 this.maskEl.top.setStyle('z-index', zIndex);
10529 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10530 this.maskEl.top.setLeft(0);
10531 this.maskEl.top.setTop(0);
10532 this.maskEl.top.show();
10534 this.maskEl.left.setStyle('position', 'absolute');
10535 this.maskEl.left.setStyle('z-index', zIndex);
10536 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10537 this.maskEl.left.setLeft(0);
10538 this.maskEl.left.setTop(box.y - this.padding);
10539 this.maskEl.left.show();
10541 this.maskEl.bottom.setStyle('position', 'absolute');
10542 this.maskEl.bottom.setStyle('z-index', zIndex);
10543 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10544 this.maskEl.bottom.setLeft(0);
10545 this.maskEl.bottom.setTop(box.bottom + this.padding);
10546 this.maskEl.bottom.show();
10548 this.maskEl.right.setStyle('position', 'absolute');
10549 this.maskEl.right.setStyle('z-index', zIndex);
10550 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10551 this.maskEl.right.setLeft(box.right + this.padding);
10552 this.maskEl.right.setTop(box.y - this.padding);
10553 this.maskEl.right.show();
10555 this.toolTip.bindEl = this.target.el;
10557 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10559 var tip = this.target.blankText;
10561 if(this.target.getValue() !== '' ) {
10563 if (this.target.invalidText.length) {
10564 tip = this.target.invalidText;
10565 } else if (this.target.regexText.length){
10566 tip = this.target.regexText;
10570 this.toolTip.show(tip);
10572 this.intervalID = window.setInterval(function() {
10573 Roo.bootstrap.Form.popover.unmask();
10576 window.onwheel = function(){ return false;};
10578 (function(){ this.isMasked = true; }).defer(500, this);
10582 unmask : function()
10584 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10588 this.maskEl.top.setStyle('position', 'absolute');
10589 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10590 this.maskEl.top.hide();
10592 this.maskEl.left.setStyle('position', 'absolute');
10593 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10594 this.maskEl.left.hide();
10596 this.maskEl.bottom.setStyle('position', 'absolute');
10597 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10598 this.maskEl.bottom.hide();
10600 this.maskEl.right.setStyle('position', 'absolute');
10601 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10602 this.maskEl.right.hide();
10604 this.toolTip.hide();
10606 this.toolTip.el.hide();
10608 window.onwheel = function(){ return true;};
10610 if(this.intervalID){
10611 window.clearInterval(this.intervalID);
10612 this.intervalID = false;
10615 this.isMasked = false;
10625 * Ext JS Library 1.1.1
10626 * Copyright(c) 2006-2007, Ext JS, LLC.
10628 * Originally Released Under LGPL - original licence link has changed is not relivant.
10631 * <script type="text/javascript">
10634 * @class Roo.form.VTypes
10635 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10638 Roo.form.VTypes = function(){
10639 // closure these in so they are only created once.
10640 var alpha = /^[a-zA-Z_]+$/;
10641 var alphanum = /^[a-zA-Z0-9_]+$/;
10642 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10643 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10645 // All these messages and functions are configurable
10648 * The function used to validate email addresses
10649 * @param {String} value The email address
10651 'email' : function(v){
10652 return email.test(v);
10655 * The error text to display when the email validation function returns false
10658 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10660 * The keystroke filter mask to be applied on email input
10663 'emailMask' : /[a-z0-9_\.\-@]/i,
10666 * The function used to validate URLs
10667 * @param {String} value The URL
10669 'url' : function(v){
10670 return url.test(v);
10673 * The error text to display when the url validation function returns false
10676 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10679 * The function used to validate alpha values
10680 * @param {String} value The value
10682 'alpha' : function(v){
10683 return alpha.test(v);
10686 * The error text to display when the alpha validation function returns false
10689 'alphaText' : 'This field should only contain letters and _',
10691 * The keystroke filter mask to be applied on alpha input
10694 'alphaMask' : /[a-z_]/i,
10697 * The function used to validate alphanumeric values
10698 * @param {String} value The value
10700 'alphanum' : function(v){
10701 return alphanum.test(v);
10704 * The error text to display when the alphanumeric validation function returns false
10707 'alphanumText' : 'This field should only contain letters, numbers and _',
10709 * The keystroke filter mask to be applied on alphanumeric input
10712 'alphanumMask' : /[a-z0-9_]/i
10722 * @class Roo.bootstrap.Input
10723 * @extends Roo.bootstrap.Component
10724 * Bootstrap Input class
10725 * @cfg {Boolean} disabled is it disabled
10726 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10727 * @cfg {String} name name of the input
10728 * @cfg {string} fieldLabel - the label associated
10729 * @cfg {string} placeholder - placeholder to put in text.
10730 * @cfg {string} before - input group add on before
10731 * @cfg {string} after - input group add on after
10732 * @cfg {string} size - (lg|sm) or leave empty..
10733 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10734 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10735 * @cfg {Number} md colspan out of 12 for computer-sized screens
10736 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10737 * @cfg {string} value default value of the input
10738 * @cfg {Number} labelWidth set the width of label
10739 * @cfg {Number} labellg set the width of label (1-12)
10740 * @cfg {Number} labelmd set the width of label (1-12)
10741 * @cfg {Number} labelsm set the width of label (1-12)
10742 * @cfg {Number} labelxs set the width of label (1-12)
10743 * @cfg {String} labelAlign (top|left)
10744 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10745 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10746 * @cfg {String} indicatorpos (left|right) default left
10747 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10748 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10749 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10751 * @cfg {String} align (left|center|right) Default left
10752 * @cfg {Boolean} forceFeedback (true|false) Default false
10755 * Create a new Input
10756 * @param {Object} config The config object
10759 Roo.bootstrap.Input = function(config){
10761 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10766 * Fires when this field receives input focus.
10767 * @param {Roo.form.Field} this
10772 * Fires when this field loses input focus.
10773 * @param {Roo.form.Field} this
10777 * @event specialkey
10778 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10779 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10780 * @param {Roo.form.Field} this
10781 * @param {Roo.EventObject} e The event object
10786 * Fires just before the field blurs if the field value has changed.
10787 * @param {Roo.form.Field} this
10788 * @param {Mixed} newValue The new value
10789 * @param {Mixed} oldValue The original value
10794 * Fires after the field has been marked as invalid.
10795 * @param {Roo.form.Field} this
10796 * @param {String} msg The validation message
10801 * Fires after the field has been validated with no errors.
10802 * @param {Roo.form.Field} this
10807 * Fires after the key up
10808 * @param {Roo.form.Field} this
10809 * @param {Roo.EventObject} e The event Object
10814 * Fires after the user pastes into input
10815 * @param {Roo.form.Field} this
10816 * @param {Roo.EventObject} e The event Object
10822 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10824 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10825 automatic validation (defaults to "keyup").
10827 validationEvent : "keyup",
10829 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10831 validateOnBlur : true,
10833 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10835 validationDelay : 250,
10837 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10839 focusClass : "x-form-focus", // not needed???
10843 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10845 invalidClass : "has-warning",
10848 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10850 validClass : "has-success",
10853 * @cfg {Boolean} hasFeedback (true|false) default true
10855 hasFeedback : true,
10858 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10860 invalidFeedbackClass : "glyphicon-warning-sign",
10863 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10865 validFeedbackClass : "glyphicon-ok",
10868 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10870 selectOnFocus : false,
10873 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10877 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10882 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10884 disableKeyFilter : false,
10887 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10891 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10895 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10897 blankText : "Please complete this mandatory field",
10900 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10904 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10906 maxLength : Number.MAX_VALUE,
10908 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10910 minLengthText : "The minimum length for this field is {0}",
10912 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10914 maxLengthText : "The maximum length for this field is {0}",
10918 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10919 * If available, this function will be called only after the basic validators all return true, and will be passed the
10920 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10924 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10925 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10926 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10930 * @cfg {String} regexText -- Depricated - use Invalid Text
10935 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10941 autocomplete: false,
10945 inputType : 'text',
10948 placeholder: false,
10953 preventMark: false,
10954 isFormField : true,
10957 labelAlign : false,
10960 formatedValue : false,
10961 forceFeedback : false,
10963 indicatorpos : 'left',
10973 parentLabelAlign : function()
10976 while (parent.parent()) {
10977 parent = parent.parent();
10978 if (typeof(parent.labelAlign) !='undefined') {
10979 return parent.labelAlign;
10986 getAutoCreate : function()
10988 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10994 if(this.inputType != 'hidden'){
10995 cfg.cls = 'form-group' //input-group
11001 type : this.inputType,
11002 value : this.value,
11003 cls : 'form-control',
11004 placeholder : this.placeholder || '',
11005 autocomplete : this.autocomplete || 'new-password'
11007 if (this.inputType == 'file') {
11008 input.style = 'overflow:hidden'; // why not in CSS?
11011 if(this.capture.length){
11012 input.capture = this.capture;
11015 if(this.accept.length){
11016 input.accept = this.accept + "/*";
11020 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11023 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11024 input.maxLength = this.maxLength;
11027 if (this.disabled) {
11028 input.disabled=true;
11031 if (this.readOnly) {
11032 input.readonly=true;
11036 input.name = this.name;
11040 input.cls += ' input-' + this.size;
11044 ['xs','sm','md','lg'].map(function(size){
11045 if (settings[size]) {
11046 cfg.cls += ' col-' + size + '-' + settings[size];
11050 var inputblock = input;
11054 cls: 'glyphicon form-control-feedback'
11057 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11060 cls : 'has-feedback',
11068 if (this.before || this.after) {
11071 cls : 'input-group',
11075 if (this.before && typeof(this.before) == 'string') {
11077 inputblock.cn.push({
11079 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11083 if (this.before && typeof(this.before) == 'object') {
11084 this.before = Roo.factory(this.before);
11086 inputblock.cn.push({
11088 cls : 'roo-input-before input-group-prepend input-group-' +
11089 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11093 inputblock.cn.push(input);
11095 if (this.after && typeof(this.after) == 'string') {
11096 inputblock.cn.push({
11098 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11102 if (this.after && typeof(this.after) == 'object') {
11103 this.after = Roo.factory(this.after);
11105 inputblock.cn.push({
11107 cls : 'roo-input-after input-group-append input-group-' +
11108 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11112 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11113 inputblock.cls += ' has-feedback';
11114 inputblock.cn.push(feedback);
11119 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11120 tooltip : 'This field is required'
11122 if (this.allowBlank ) {
11123 indicator.style = this.allowBlank ? ' display:none' : '';
11125 if (align ==='left' && this.fieldLabel.length) {
11127 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11134 cls : 'control-label col-form-label',
11135 html : this.fieldLabel
11146 var labelCfg = cfg.cn[1];
11147 var contentCfg = cfg.cn[2];
11149 if(this.indicatorpos == 'right'){
11154 cls : 'control-label col-form-label',
11158 html : this.fieldLabel
11172 labelCfg = cfg.cn[0];
11173 contentCfg = cfg.cn[1];
11177 if(this.labelWidth > 12){
11178 labelCfg.style = "width: " + this.labelWidth + 'px';
11181 if(this.labelWidth < 13 && this.labelmd == 0){
11182 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11185 if(this.labellg > 0){
11186 labelCfg.cls += ' col-lg-' + this.labellg;
11187 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11190 if(this.labelmd > 0){
11191 labelCfg.cls += ' col-md-' + this.labelmd;
11192 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11195 if(this.labelsm > 0){
11196 labelCfg.cls += ' col-sm-' + this.labelsm;
11197 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11200 if(this.labelxs > 0){
11201 labelCfg.cls += ' col-xs-' + this.labelxs;
11202 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11206 } else if ( this.fieldLabel.length) {
11213 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11214 tooltip : 'This field is required',
11215 style : this.allowBlank ? ' display:none' : ''
11219 //cls : 'input-group-addon',
11220 html : this.fieldLabel
11228 if(this.indicatorpos == 'right'){
11233 //cls : 'input-group-addon',
11234 html : this.fieldLabel
11239 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11240 tooltip : 'This field is required',
11241 style : this.allowBlank ? ' display:none' : ''
11261 if (this.parentType === 'Navbar' && this.parent().bar) {
11262 cfg.cls += ' navbar-form';
11265 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11266 // on BS4 we do this only if not form
11267 cfg.cls += ' navbar-form';
11275 * return the real input element.
11277 inputEl: function ()
11279 return this.el.select('input.form-control',true).first();
11282 tooltipEl : function()
11284 return this.inputEl();
11287 indicatorEl : function()
11289 if (Roo.bootstrap.version == 4) {
11290 return false; // not enabled in v4 yet.
11293 var indicator = this.el.select('i.roo-required-indicator',true).first();
11303 setDisabled : function(v)
11305 var i = this.inputEl().dom;
11307 i.removeAttribute('disabled');
11311 i.setAttribute('disabled','true');
11313 initEvents : function()
11316 this.inputEl().on("keydown" , this.fireKey, this);
11317 this.inputEl().on("focus", this.onFocus, this);
11318 this.inputEl().on("blur", this.onBlur, this);
11320 this.inputEl().relayEvent('keyup', this);
11321 this.inputEl().relayEvent('paste', this);
11323 this.indicator = this.indicatorEl();
11325 if(this.indicator){
11326 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11329 // reference to original value for reset
11330 this.originalValue = this.getValue();
11331 //Roo.form.TextField.superclass.initEvents.call(this);
11332 if(this.validationEvent == 'keyup'){
11333 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11334 this.inputEl().on('keyup', this.filterValidation, this);
11336 else if(this.validationEvent !== false){
11337 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11340 if(this.selectOnFocus){
11341 this.on("focus", this.preFocus, this);
11344 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11345 this.inputEl().on("keypress", this.filterKeys, this);
11347 this.inputEl().relayEvent('keypress', this);
11350 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11351 this.el.on("click", this.autoSize, this);
11354 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11355 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11358 if (typeof(this.before) == 'object') {
11359 this.before.render(this.el.select('.roo-input-before',true).first());
11361 if (typeof(this.after) == 'object') {
11362 this.after.render(this.el.select('.roo-input-after',true).first());
11365 this.inputEl().on('change', this.onChange, this);
11368 filterValidation : function(e){
11369 if(!e.isNavKeyPress()){
11370 this.validationTask.delay(this.validationDelay);
11374 * Validates the field value
11375 * @return {Boolean} True if the value is valid, else false
11377 validate : function(){
11378 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11379 if(this.disabled || this.validateValue(this.getRawValue())){
11384 this.markInvalid();
11390 * Validates a value according to the field's validation rules and marks the field as invalid
11391 * if the validation fails
11392 * @param {Mixed} value The value to validate
11393 * @return {Boolean} True if the value is valid, else false
11395 validateValue : function(value)
11397 if(this.getVisibilityEl().hasClass('hidden')){
11401 if(value.length < 1) { // if it's blank
11402 if(this.allowBlank){
11408 if(value.length < this.minLength){
11411 if(value.length > this.maxLength){
11415 var vt = Roo.form.VTypes;
11416 if(!vt[this.vtype](value, this)){
11420 if(typeof this.validator == "function"){
11421 var msg = this.validator(value);
11425 if (typeof(msg) == 'string') {
11426 this.invalidText = msg;
11430 if(this.regex && !this.regex.test(value)){
11438 fireKey : function(e){
11439 //Roo.log('field ' + e.getKey());
11440 if(e.isNavKeyPress()){
11441 this.fireEvent("specialkey", this, e);
11444 focus : function (selectText){
11446 this.inputEl().focus();
11447 if(selectText === true){
11448 this.inputEl().dom.select();
11454 onFocus : function(){
11455 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11456 // this.el.addClass(this.focusClass);
11458 if(!this.hasFocus){
11459 this.hasFocus = true;
11460 this.startValue = this.getValue();
11461 this.fireEvent("focus", this);
11465 beforeBlur : Roo.emptyFn,
11469 onBlur : function(){
11471 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11472 //this.el.removeClass(this.focusClass);
11474 this.hasFocus = false;
11475 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11478 var v = this.getValue();
11479 if(String(v) !== String(this.startValue)){
11480 this.fireEvent('change', this, v, this.startValue);
11482 this.fireEvent("blur", this);
11485 onChange : function(e)
11487 var v = this.getValue();
11488 if(String(v) !== String(this.startValue)){
11489 this.fireEvent('change', this, v, this.startValue);
11495 * Resets the current field value to the originally loaded value and clears any validation messages
11497 reset : function(){
11498 this.setValue(this.originalValue);
11502 * Returns the name of the field
11503 * @return {Mixed} name The name field
11505 getName: function(){
11509 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11510 * @return {Mixed} value The field value
11512 getValue : function(){
11514 var v = this.inputEl().getValue();
11519 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11520 * @return {Mixed} value The field value
11522 getRawValue : function(){
11523 var v = this.inputEl().getValue();
11529 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11530 * @param {Mixed} value The value to set
11532 setRawValue : function(v){
11533 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11536 selectText : function(start, end){
11537 var v = this.getRawValue();
11539 start = start === undefined ? 0 : start;
11540 end = end === undefined ? v.length : end;
11541 var d = this.inputEl().dom;
11542 if(d.setSelectionRange){
11543 d.setSelectionRange(start, end);
11544 }else if(d.createTextRange){
11545 var range = d.createTextRange();
11546 range.moveStart("character", start);
11547 range.moveEnd("character", v.length-end);
11554 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11555 * @param {Mixed} value The value to set
11557 setValue : function(v){
11560 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11566 processValue : function(value){
11567 if(this.stripCharsRe){
11568 var newValue = value.replace(this.stripCharsRe, '');
11569 if(newValue !== value){
11570 this.setRawValue(newValue);
11577 preFocus : function(){
11579 if(this.selectOnFocus){
11580 this.inputEl().dom.select();
11583 filterKeys : function(e){
11584 var k = e.getKey();
11585 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11588 var c = e.getCharCode(), cc = String.fromCharCode(c);
11589 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11592 if(!this.maskRe.test(cc)){
11597 * Clear any invalid styles/messages for this field
11599 clearInvalid : function(){
11601 if(!this.el || this.preventMark){ // not rendered
11606 this.el.removeClass([this.invalidClass, 'is-invalid']);
11608 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11610 var feedback = this.el.select('.form-control-feedback', true).first();
11613 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11618 if(this.indicator){
11619 this.indicator.removeClass('visible');
11620 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11623 this.fireEvent('valid', this);
11627 * Mark this field as valid
11629 markValid : function()
11631 if(!this.el || this.preventMark){ // not rendered...
11635 this.el.removeClass([this.invalidClass, this.validClass]);
11636 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11638 var feedback = this.el.select('.form-control-feedback', true).first();
11641 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11644 if(this.indicator){
11645 this.indicator.removeClass('visible');
11646 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11654 if(this.allowBlank && !this.getRawValue().length){
11657 if (Roo.bootstrap.version == 3) {
11658 this.el.addClass(this.validClass);
11660 this.inputEl().addClass('is-valid');
11663 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11665 var feedback = this.el.select('.form-control-feedback', true).first();
11668 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11669 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11674 this.fireEvent('valid', this);
11678 * Mark this field as invalid
11679 * @param {String} msg The validation message
11681 markInvalid : function(msg)
11683 if(!this.el || this.preventMark){ // not rendered
11687 this.el.removeClass([this.invalidClass, this.validClass]);
11688 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11690 var feedback = this.el.select('.form-control-feedback', true).first();
11693 this.el.select('.form-control-feedback', true).first().removeClass(
11694 [this.invalidFeedbackClass, this.validFeedbackClass]);
11701 if(this.allowBlank && !this.getRawValue().length){
11705 if(this.indicator){
11706 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11707 this.indicator.addClass('visible');
11709 if (Roo.bootstrap.version == 3) {
11710 this.el.addClass(this.invalidClass);
11712 this.inputEl().addClass('is-invalid');
11717 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11719 var feedback = this.el.select('.form-control-feedback', true).first();
11722 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11724 if(this.getValue().length || this.forceFeedback){
11725 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11732 this.fireEvent('invalid', this, msg);
11735 SafariOnKeyDown : function(event)
11737 // this is a workaround for a password hang bug on chrome/ webkit.
11738 if (this.inputEl().dom.type != 'password') {
11742 var isSelectAll = false;
11744 if(this.inputEl().dom.selectionEnd > 0){
11745 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11747 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11748 event.preventDefault();
11753 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11755 event.preventDefault();
11756 // this is very hacky as keydown always get's upper case.
11758 var cc = String.fromCharCode(event.getCharCode());
11759 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11763 adjustWidth : function(tag, w){
11764 tag = tag.toLowerCase();
11765 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11766 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11767 if(tag == 'input'){
11770 if(tag == 'textarea'){
11773 }else if(Roo.isOpera){
11774 if(tag == 'input'){
11777 if(tag == 'textarea'){
11785 setFieldLabel : function(v)
11787 if(!this.rendered){
11791 if(this.indicatorEl()){
11792 var ar = this.el.select('label > span',true);
11794 if (ar.elements.length) {
11795 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11796 this.fieldLabel = v;
11800 var br = this.el.select('label',true);
11802 if(br.elements.length) {
11803 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11804 this.fieldLabel = v;
11808 Roo.log('Cannot Found any of label > span || label in input');
11812 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11813 this.fieldLabel = v;
11828 * @class Roo.bootstrap.TextArea
11829 * @extends Roo.bootstrap.Input
11830 * Bootstrap TextArea class
11831 * @cfg {Number} cols Specifies the visible width of a text area
11832 * @cfg {Number} rows Specifies the visible number of lines in a text area
11833 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11834 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11835 * @cfg {string} html text
11838 * Create a new TextArea
11839 * @param {Object} config The config object
11842 Roo.bootstrap.TextArea = function(config){
11843 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11847 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11857 getAutoCreate : function(){
11859 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11865 if(this.inputType != 'hidden'){
11866 cfg.cls = 'form-group' //input-group
11874 value : this.value || '',
11875 html: this.html || '',
11876 cls : 'form-control',
11877 placeholder : this.placeholder || ''
11881 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11882 input.maxLength = this.maxLength;
11886 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11890 input.cols = this.cols;
11893 if (this.readOnly) {
11894 input.readonly = true;
11898 input.name = this.name;
11902 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11906 ['xs','sm','md','lg'].map(function(size){
11907 if (settings[size]) {
11908 cfg.cls += ' col-' + size + '-' + settings[size];
11912 var inputblock = input;
11914 if(this.hasFeedback && !this.allowBlank){
11918 cls: 'glyphicon form-control-feedback'
11922 cls : 'has-feedback',
11931 if (this.before || this.after) {
11934 cls : 'input-group',
11938 inputblock.cn.push({
11940 cls : 'input-group-addon',
11945 inputblock.cn.push(input);
11947 if(this.hasFeedback && !this.allowBlank){
11948 inputblock.cls += ' has-feedback';
11949 inputblock.cn.push(feedback);
11953 inputblock.cn.push({
11955 cls : 'input-group-addon',
11962 if (align ==='left' && this.fieldLabel.length) {
11967 cls : 'control-label',
11968 html : this.fieldLabel
11979 if(this.labelWidth > 12){
11980 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11983 if(this.labelWidth < 13 && this.labelmd == 0){
11984 this.labelmd = this.labelWidth;
11987 if(this.labellg > 0){
11988 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11989 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11992 if(this.labelmd > 0){
11993 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11994 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11997 if(this.labelsm > 0){
11998 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11999 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12002 if(this.labelxs > 0){
12003 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12004 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12007 } else if ( this.fieldLabel.length) {
12012 //cls : 'input-group-addon',
12013 html : this.fieldLabel
12031 if (this.disabled) {
12032 input.disabled=true;
12039 * return the real textarea element.
12041 inputEl: function ()
12043 return this.el.select('textarea.form-control',true).first();
12047 * Clear any invalid styles/messages for this field
12049 clearInvalid : function()
12052 if(!this.el || this.preventMark){ // not rendered
12056 var label = this.el.select('label', true).first();
12057 var icon = this.el.select('i.fa-star', true).first();
12062 this.el.removeClass( this.validClass);
12063 this.inputEl().removeClass('is-invalid');
12065 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12067 var feedback = this.el.select('.form-control-feedback', true).first();
12070 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12075 this.fireEvent('valid', this);
12079 * Mark this field as valid
12081 markValid : function()
12083 if(!this.el || this.preventMark){ // not rendered
12087 this.el.removeClass([this.invalidClass, this.validClass]);
12088 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12090 var feedback = this.el.select('.form-control-feedback', true).first();
12093 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12096 if(this.disabled || this.allowBlank){
12100 var label = this.el.select('label', true).first();
12101 var icon = this.el.select('i.fa-star', true).first();
12106 if (Roo.bootstrap.version == 3) {
12107 this.el.addClass(this.validClass);
12109 this.inputEl().addClass('is-valid');
12113 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12115 var feedback = this.el.select('.form-control-feedback', true).first();
12118 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12119 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12124 this.fireEvent('valid', this);
12128 * Mark this field as invalid
12129 * @param {String} msg The validation message
12131 markInvalid : function(msg)
12133 if(!this.el || this.preventMark){ // not rendered
12137 this.el.removeClass([this.invalidClass, this.validClass]);
12138 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12140 var feedback = this.el.select('.form-control-feedback', true).first();
12143 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12146 if(this.disabled || this.allowBlank){
12150 var label = this.el.select('label', true).first();
12151 var icon = this.el.select('i.fa-star', true).first();
12153 if(!this.getValue().length && label && !icon){
12154 this.el.createChild({
12156 cls : 'text-danger fa fa-lg fa-star',
12157 tooltip : 'This field is required',
12158 style : 'margin-right:5px;'
12162 if (Roo.bootstrap.version == 3) {
12163 this.el.addClass(this.invalidClass);
12165 this.inputEl().addClass('is-invalid');
12168 // fixme ... this may be depricated need to test..
12169 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12171 var feedback = this.el.select('.form-control-feedback', true).first();
12174 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12176 if(this.getValue().length || this.forceFeedback){
12177 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12184 this.fireEvent('invalid', this, msg);
12192 * trigger field - base class for combo..
12197 * @class Roo.bootstrap.TriggerField
12198 * @extends Roo.bootstrap.Input
12199 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12200 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12201 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12202 * for which you can provide a custom implementation. For example:
12204 var trigger = new Roo.bootstrap.TriggerField();
12205 trigger.onTriggerClick = myTriggerFn;
12206 trigger.applyTo('my-field');
12209 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12210 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12211 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12212 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12213 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12216 * Create a new TriggerField.
12217 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12218 * to the base TextField)
12220 Roo.bootstrap.TriggerField = function(config){
12221 this.mimicing = false;
12222 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12225 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12227 * @cfg {String} triggerClass A CSS class to apply to the trigger
12230 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12235 * @cfg {Boolean} removable (true|false) special filter default false
12239 /** @cfg {Boolean} grow @hide */
12240 /** @cfg {Number} growMin @hide */
12241 /** @cfg {Number} growMax @hide */
12247 autoSize: Roo.emptyFn,
12251 deferHeight : true,
12254 actionMode : 'wrap',
12259 getAutoCreate : function(){
12261 var align = this.labelAlign || this.parentLabelAlign();
12266 cls: 'form-group' //input-group
12273 type : this.inputType,
12274 cls : 'form-control',
12275 autocomplete: 'new-password',
12276 placeholder : this.placeholder || ''
12280 input.name = this.name;
12283 input.cls += ' input-' + this.size;
12286 if (this.disabled) {
12287 input.disabled=true;
12290 var inputblock = input;
12292 if(this.hasFeedback && !this.allowBlank){
12296 cls: 'glyphicon form-control-feedback'
12299 if(this.removable && !this.editable ){
12301 cls : 'has-feedback',
12307 cls : 'roo-combo-removable-btn close'
12314 cls : 'has-feedback',
12323 if(this.removable && !this.editable ){
12325 cls : 'roo-removable',
12331 cls : 'roo-combo-removable-btn close'
12338 if (this.before || this.after) {
12341 cls : 'input-group',
12345 inputblock.cn.push({
12347 cls : 'input-group-addon input-group-prepend input-group-text',
12352 inputblock.cn.push(input);
12354 if(this.hasFeedback && !this.allowBlank){
12355 inputblock.cls += ' has-feedback';
12356 inputblock.cn.push(feedback);
12360 inputblock.cn.push({
12362 cls : 'input-group-addon input-group-append input-group-text',
12371 var ibwrap = inputblock;
12376 cls: 'roo-select2-choices',
12380 cls: 'roo-select2-search-field',
12392 cls: 'roo-select2-container input-group',
12397 cls: 'form-hidden-field'
12403 if(!this.multiple && this.showToggleBtn){
12409 if (this.caret != false) {
12412 cls: 'fa fa-' + this.caret
12419 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12421 Roo.bootstrap.version == 3 ? caret : '',
12424 cls: 'combobox-clear',
12438 combobox.cls += ' roo-select2-container-multi';
12442 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12443 tooltip : 'This field is required'
12445 if (Roo.bootstrap.version == 4) {
12448 style : 'display:none'
12453 if (align ==='left' && this.fieldLabel.length) {
12455 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12462 cls : 'control-label',
12463 html : this.fieldLabel
12475 var labelCfg = cfg.cn[1];
12476 var contentCfg = cfg.cn[2];
12478 if(this.indicatorpos == 'right'){
12483 cls : 'control-label',
12487 html : this.fieldLabel
12501 labelCfg = cfg.cn[0];
12502 contentCfg = cfg.cn[1];
12505 if(this.labelWidth > 12){
12506 labelCfg.style = "width: " + this.labelWidth + 'px';
12509 if(this.labelWidth < 13 && this.labelmd == 0){
12510 this.labelmd = this.labelWidth;
12513 if(this.labellg > 0){
12514 labelCfg.cls += ' col-lg-' + this.labellg;
12515 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12518 if(this.labelmd > 0){
12519 labelCfg.cls += ' col-md-' + this.labelmd;
12520 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12523 if(this.labelsm > 0){
12524 labelCfg.cls += ' col-sm-' + this.labelsm;
12525 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12528 if(this.labelxs > 0){
12529 labelCfg.cls += ' col-xs-' + this.labelxs;
12530 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12533 } else if ( this.fieldLabel.length) {
12534 // Roo.log(" label");
12539 //cls : 'input-group-addon',
12540 html : this.fieldLabel
12548 if(this.indicatorpos == 'right'){
12556 html : this.fieldLabel
12570 // Roo.log(" no label && no align");
12577 ['xs','sm','md','lg'].map(function(size){
12578 if (settings[size]) {
12579 cfg.cls += ' col-' + size + '-' + settings[size];
12590 onResize : function(w, h){
12591 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12592 // if(typeof w == 'number'){
12593 // var x = w - this.trigger.getWidth();
12594 // this.inputEl().setWidth(this.adjustWidth('input', x));
12595 // this.trigger.setStyle('left', x+'px');
12600 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12603 getResizeEl : function(){
12604 return this.inputEl();
12608 getPositionEl : function(){
12609 return this.inputEl();
12613 alignErrorIcon : function(){
12614 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12618 initEvents : function(){
12622 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12623 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12624 if(!this.multiple && this.showToggleBtn){
12625 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12626 if(this.hideTrigger){
12627 this.trigger.setDisplayed(false);
12629 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12633 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12636 if(this.removable && !this.editable && !this.tickable){
12637 var close = this.closeTriggerEl();
12640 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12641 close.on('click', this.removeBtnClick, this, close);
12645 //this.trigger.addClassOnOver('x-form-trigger-over');
12646 //this.trigger.addClassOnClick('x-form-trigger-click');
12649 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12653 closeTriggerEl : function()
12655 var close = this.el.select('.roo-combo-removable-btn', true).first();
12656 return close ? close : false;
12659 removeBtnClick : function(e, h, el)
12661 e.preventDefault();
12663 if(this.fireEvent("remove", this) !== false){
12665 this.fireEvent("afterremove", this)
12669 createList : function()
12671 this.list = Roo.get(document.body).createChild({
12672 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12673 cls: 'typeahead typeahead-long dropdown-menu shadow',
12674 style: 'display:none'
12677 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12682 initTrigger : function(){
12687 onDestroy : function(){
12689 this.trigger.removeAllListeners();
12690 // this.trigger.remove();
12693 // this.wrap.remove();
12695 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12699 onFocus : function(){
12700 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12702 if(!this.mimicing){
12703 this.wrap.addClass('x-trigger-wrap-focus');
12704 this.mimicing = true;
12705 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12706 if(this.monitorTab){
12707 this.el.on("keydown", this.checkTab, this);
12714 checkTab : function(e){
12715 if(e.getKey() == e.TAB){
12716 this.triggerBlur();
12721 onBlur : function(){
12726 mimicBlur : function(e, t){
12728 if(!this.wrap.contains(t) && this.validateBlur()){
12729 this.triggerBlur();
12735 triggerBlur : function(){
12736 this.mimicing = false;
12737 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12738 if(this.monitorTab){
12739 this.el.un("keydown", this.checkTab, this);
12741 //this.wrap.removeClass('x-trigger-wrap-focus');
12742 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12746 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12747 validateBlur : function(e, t){
12752 onDisable : function(){
12753 this.inputEl().dom.disabled = true;
12754 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12756 // this.wrap.addClass('x-item-disabled');
12761 onEnable : function(){
12762 this.inputEl().dom.disabled = false;
12763 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12765 // this.el.removeClass('x-item-disabled');
12770 onShow : function(){
12771 var ae = this.getActionEl();
12774 ae.dom.style.display = '';
12775 ae.dom.style.visibility = 'visible';
12781 onHide : function(){
12782 var ae = this.getActionEl();
12783 ae.dom.style.display = 'none';
12787 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12788 * by an implementing function.
12790 * @param {EventObject} e
12792 onTriggerClick : Roo.emptyFn
12800 * @class Roo.bootstrap.CardUploader
12801 * @extends Roo.bootstrap.Button
12802 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12803 * @cfg {Number} errorTimeout default 3000
12804 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12805 * @cfg {Array} html The button text.
12809 * Create a new CardUploader
12810 * @param {Object} config The config object
12813 Roo.bootstrap.CardUploader = function(config){
12817 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12820 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12828 * When a image is clicked on - and needs to display a slideshow or similar..
12829 * @param {Roo.bootstrap.Card} this
12830 * @param {Object} The image information data
12836 * When a the download link is clicked
12837 * @param {Roo.bootstrap.Card} this
12838 * @param {Object} The image information data contains
12845 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12848 errorTimeout : 3000,
12852 fileCollection : false,
12855 getAutoCreate : function()
12859 cls :'form-group' ,
12864 //cls : 'input-group-addon',
12865 html : this.fieldLabel
12873 value : this.value,
12874 cls : 'd-none form-control'
12879 multiple : 'multiple',
12881 cls : 'd-none roo-card-upload-selector'
12885 cls : 'roo-card-uploader-button-container w-100 mb-2'
12888 cls : 'card-columns roo-card-uploader-container'
12898 getChildContainer : function() /// what children are added to.
12900 return this.containerEl;
12903 getButtonContainer : function() /// what children are added to.
12905 return this.el.select(".roo-card-uploader-button-container").first();
12908 initEvents : function()
12911 Roo.bootstrap.Input.prototype.initEvents.call(this);
12915 xns: Roo.bootstrap,
12918 container_method : 'getButtonContainer' ,
12919 html : this.html, // fix changable?
12922 'click' : function(btn, e) {
12931 this.urlAPI = (window.createObjectURL && window) ||
12932 (window.URL && URL.revokeObjectURL && URL) ||
12933 (window.webkitURL && webkitURL);
12938 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12940 this.selectorEl.on('change', this.onFileSelected, this);
12943 this.images.forEach(function(img) {
12946 this.images = false;
12948 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12954 onClick : function(e)
12956 e.preventDefault();
12958 this.selectorEl.dom.click();
12962 onFileSelected : function(e)
12964 e.preventDefault();
12966 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12970 Roo.each(this.selectorEl.dom.files, function(file){
12971 this.addFile(file);
12980 addFile : function(file)
12983 if(typeof(file) === 'string'){
12984 throw "Add file by name?"; // should not happen
12988 if(!file || !this.urlAPI){
12998 var url = _this.urlAPI.createObjectURL( file);
13001 id : Roo.bootstrap.CardUploader.ID--,
13002 is_uploaded : false,
13006 mimetype : file.type,
13014 * addCard - add an Attachment to the uploader
13015 * @param data - the data about the image to upload
13019 title : "Title of file",
13020 is_uploaded : false,
13021 src : "http://.....",
13022 srcfile : { the File upload object },
13023 mimetype : file.type,
13026 .. any other data...
13032 addCard : function (data)
13034 // hidden input element?
13035 // if the file is not an image...
13036 //then we need to use something other that and header_image
13041 xns : Roo.bootstrap,
13042 xtype : 'CardFooter',
13045 xns : Roo.bootstrap,
13051 xns : Roo.bootstrap,
13053 html : String.format("<small>{0}</small>", data.title),
13054 cls : 'col-10 text-left',
13059 click : function() {
13061 t.fireEvent( "download", t, data );
13067 xns : Roo.bootstrap,
13069 style: 'max-height: 28px; ',
13075 click : function() {
13076 t.removeCard(data.id)
13088 var cn = this.addxtype(
13091 xns : Roo.bootstrap,
13094 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13095 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13096 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13101 initEvents : function() {
13102 Roo.bootstrap.Card.prototype.initEvents.call(this);
13104 this.imgEl = this.el.select('.card-img-top').first();
13106 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13107 this.imgEl.set({ 'pointer' : 'cursor' });
13110 this.getCardFooter().addClass('p-1');
13117 // dont' really need ot update items.
13118 // this.items.push(cn);
13119 this.fileCollection.add(cn);
13121 if (!data.srcfile) {
13122 this.updateInput();
13127 var reader = new FileReader();
13128 reader.addEventListener("load", function() {
13129 data.srcdata = reader.result;
13132 reader.readAsDataURL(data.srcfile);
13137 removeCard : function(id)
13140 var card = this.fileCollection.get(id);
13141 card.data.is_deleted = 1;
13142 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13143 //this.fileCollection.remove(card);
13144 //this.items = this.items.filter(function(e) { return e != card });
13145 // dont' really need ot update items.
13146 card.el.dom.parentNode.removeChild(card.el.dom);
13147 this.updateInput();
13153 this.fileCollection.each(function(card) {
13154 if (card.el.dom && card.el.dom.parentNode) {
13155 card.el.dom.parentNode.removeChild(card.el.dom);
13158 this.fileCollection.clear();
13159 this.updateInput();
13162 updateInput : function()
13165 this.fileCollection.each(function(e) {
13169 this.inputEl().dom.value = JSON.stringify(data);
13179 Roo.bootstrap.CardUploader.ID = -1;/*
13181 * Ext JS Library 1.1.1
13182 * Copyright(c) 2006-2007, Ext JS, LLC.
13184 * Originally Released Under LGPL - original licence link has changed is not relivant.
13187 * <script type="text/javascript">
13192 * @class Roo.data.SortTypes
13194 * Defines the default sorting (casting?) comparison functions used when sorting data.
13196 Roo.data.SortTypes = {
13198 * Default sort that does nothing
13199 * @param {Mixed} s The value being converted
13200 * @return {Mixed} The comparison value
13202 none : function(s){
13207 * The regular expression used to strip tags
13211 stripTagsRE : /<\/?[^>]+>/gi,
13214 * Strips all HTML tags to sort on text only
13215 * @param {Mixed} s The value being converted
13216 * @return {String} The comparison value
13218 asText : function(s){
13219 return String(s).replace(this.stripTagsRE, "");
13223 * Strips all HTML tags to sort on text only - Case insensitive
13224 * @param {Mixed} s The value being converted
13225 * @return {String} The comparison value
13227 asUCText : function(s){
13228 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13232 * Case insensitive string
13233 * @param {Mixed} s The value being converted
13234 * @return {String} The comparison value
13236 asUCString : function(s) {
13237 return String(s).toUpperCase();
13242 * @param {Mixed} s The value being converted
13243 * @return {Number} The comparison value
13245 asDate : function(s) {
13249 if(s instanceof Date){
13250 return s.getTime();
13252 return Date.parse(String(s));
13257 * @param {Mixed} s The value being converted
13258 * @return {Float} The comparison value
13260 asFloat : function(s) {
13261 var val = parseFloat(String(s).replace(/,/g, ""));
13270 * @param {Mixed} s The value being converted
13271 * @return {Number} The comparison value
13273 asInt : function(s) {
13274 var val = parseInt(String(s).replace(/,/g, ""));
13282 * Ext JS Library 1.1.1
13283 * Copyright(c) 2006-2007, Ext JS, LLC.
13285 * Originally Released Under LGPL - original licence link has changed is not relivant.
13288 * <script type="text/javascript">
13292 * @class Roo.data.Record
13293 * Instances of this class encapsulate both record <em>definition</em> information, and record
13294 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13295 * to access Records cached in an {@link Roo.data.Store} object.<br>
13297 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13298 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13301 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13303 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13304 * {@link #create}. The parameters are the same.
13305 * @param {Array} data An associative Array of data values keyed by the field name.
13306 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13307 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13308 * not specified an integer id is generated.
13310 Roo.data.Record = function(data, id){
13311 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13316 * Generate a constructor for a specific record layout.
13317 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13318 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13319 * Each field definition object may contain the following properties: <ul>
13320 * <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,
13321 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13322 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13323 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13324 * is being used, then this is a string containing the javascript expression to reference the data relative to
13325 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13326 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13327 * this may be omitted.</p></li>
13328 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13329 * <ul><li>auto (Default, implies no conversion)</li>
13334 * <li>date</li></ul></p></li>
13335 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13336 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13337 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13338 * by the Reader into an object that will be stored in the Record. It is passed the
13339 * following parameters:<ul>
13340 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13342 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13344 * <br>usage:<br><pre><code>
13345 var TopicRecord = Roo.data.Record.create(
13346 {name: 'title', mapping: 'topic_title'},
13347 {name: 'author', mapping: 'username'},
13348 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13349 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13350 {name: 'lastPoster', mapping: 'user2'},
13351 {name: 'excerpt', mapping: 'post_text'}
13354 var myNewRecord = new TopicRecord({
13355 title: 'Do my job please',
13358 lastPost: new Date(),
13359 lastPoster: 'Animal',
13360 excerpt: 'No way dude!'
13362 myStore.add(myNewRecord);
13367 Roo.data.Record.create = function(o){
13368 var f = function(){
13369 f.superclass.constructor.apply(this, arguments);
13371 Roo.extend(f, Roo.data.Record);
13372 var p = f.prototype;
13373 p.fields = new Roo.util.MixedCollection(false, function(field){
13376 for(var i = 0, len = o.length; i < len; i++){
13377 p.fields.add(new Roo.data.Field(o[i]));
13379 f.getField = function(name){
13380 return p.fields.get(name);
13385 Roo.data.Record.AUTO_ID = 1000;
13386 Roo.data.Record.EDIT = 'edit';
13387 Roo.data.Record.REJECT = 'reject';
13388 Roo.data.Record.COMMIT = 'commit';
13390 Roo.data.Record.prototype = {
13392 * Readonly flag - true if this record has been modified.
13401 join : function(store){
13402 this.store = store;
13406 * Set the named field to the specified value.
13407 * @param {String} name The name of the field to set.
13408 * @param {Object} value The value to set the field to.
13410 set : function(name, value){
13411 if(this.data[name] == value){
13415 if(!this.modified){
13416 this.modified = {};
13418 if(typeof this.modified[name] == 'undefined'){
13419 this.modified[name] = this.data[name];
13421 this.data[name] = value;
13422 if(!this.editing && this.store){
13423 this.store.afterEdit(this);
13428 * Get the value of the named field.
13429 * @param {String} name The name of the field to get the value of.
13430 * @return {Object} The value of the field.
13432 get : function(name){
13433 return this.data[name];
13437 beginEdit : function(){
13438 this.editing = true;
13439 this.modified = {};
13443 cancelEdit : function(){
13444 this.editing = false;
13445 delete this.modified;
13449 endEdit : function(){
13450 this.editing = false;
13451 if(this.dirty && this.store){
13452 this.store.afterEdit(this);
13457 * Usually called by the {@link Roo.data.Store} which owns the Record.
13458 * Rejects all changes made to the Record since either creation, or the last commit operation.
13459 * Modified fields are reverted to their original values.
13461 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13462 * of reject operations.
13464 reject : function(){
13465 var m = this.modified;
13467 if(typeof m[n] != "function"){
13468 this.data[n] = m[n];
13471 this.dirty = false;
13472 delete this.modified;
13473 this.editing = false;
13475 this.store.afterReject(this);
13480 * Usually called by the {@link Roo.data.Store} which owns the Record.
13481 * Commits all changes made to the Record since either creation, or the last commit operation.
13483 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13484 * of commit operations.
13486 commit : function(){
13487 this.dirty = false;
13488 delete this.modified;
13489 this.editing = false;
13491 this.store.afterCommit(this);
13496 hasError : function(){
13497 return this.error != null;
13501 clearError : function(){
13506 * Creates a copy of this record.
13507 * @param {String} id (optional) A new record id if you don't want to use this record's id
13510 copy : function(newId) {
13511 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13515 * Ext JS Library 1.1.1
13516 * Copyright(c) 2006-2007, Ext JS, LLC.
13518 * Originally Released Under LGPL - original licence link has changed is not relivant.
13521 * <script type="text/javascript">
13527 * @class Roo.data.Store
13528 * @extends Roo.util.Observable
13529 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13530 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13532 * 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
13533 * has no knowledge of the format of the data returned by the Proxy.<br>
13535 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13536 * instances from the data object. These records are cached and made available through accessor functions.
13538 * Creates a new Store.
13539 * @param {Object} config A config object containing the objects needed for the Store to access data,
13540 * and read the data into Records.
13542 Roo.data.Store = function(config){
13543 this.data = new Roo.util.MixedCollection(false);
13544 this.data.getKey = function(o){
13547 this.baseParams = {};
13549 this.paramNames = {
13554 "multisort" : "_multisort"
13557 if(config && config.data){
13558 this.inlineData = config.data;
13559 delete config.data;
13562 Roo.apply(this, config);
13564 if(this.reader){ // reader passed
13565 this.reader = Roo.factory(this.reader, Roo.data);
13566 this.reader.xmodule = this.xmodule || false;
13567 if(!this.recordType){
13568 this.recordType = this.reader.recordType;
13570 if(this.reader.onMetaChange){
13571 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13575 if(this.recordType){
13576 this.fields = this.recordType.prototype.fields;
13578 this.modified = [];
13582 * @event datachanged
13583 * Fires when the data cache has changed, and a widget which is using this Store
13584 * as a Record cache should refresh its view.
13585 * @param {Store} this
13587 datachanged : true,
13589 * @event metachange
13590 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13591 * @param {Store} this
13592 * @param {Object} meta The JSON metadata
13597 * Fires when Records have been added to the Store
13598 * @param {Store} this
13599 * @param {Roo.data.Record[]} records The array of Records added
13600 * @param {Number} index The index at which the record(s) were added
13605 * Fires when a Record has been removed from the Store
13606 * @param {Store} this
13607 * @param {Roo.data.Record} record The Record that was removed
13608 * @param {Number} index The index at which the record was removed
13613 * Fires when a Record has been updated
13614 * @param {Store} this
13615 * @param {Roo.data.Record} record The Record that was updated
13616 * @param {String} operation The update operation being performed. Value may be one of:
13618 Roo.data.Record.EDIT
13619 Roo.data.Record.REJECT
13620 Roo.data.Record.COMMIT
13626 * Fires when the data cache has been cleared.
13627 * @param {Store} this
13631 * @event beforeload
13632 * Fires before a request is made for a new data object. If the beforeload handler returns false
13633 * the load action will be canceled.
13634 * @param {Store} this
13635 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13639 * @event beforeloadadd
13640 * Fires after a new set of Records has been loaded.
13641 * @param {Store} this
13642 * @param {Roo.data.Record[]} records The Records that were loaded
13643 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13645 beforeloadadd : true,
13648 * Fires after a new set of Records has been loaded, before they are added to the store.
13649 * @param {Store} this
13650 * @param {Roo.data.Record[]} records The Records that were loaded
13651 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13652 * @params {Object} return from reader
13656 * @event loadexception
13657 * Fires if an exception occurs in the Proxy during loading.
13658 * Called with the signature of the Proxy's "loadexception" event.
13659 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13662 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13663 * @param {Object} load options
13664 * @param {Object} jsonData from your request (normally this contains the Exception)
13666 loadexception : true
13670 this.proxy = Roo.factory(this.proxy, Roo.data);
13671 this.proxy.xmodule = this.xmodule || false;
13672 this.relayEvents(this.proxy, ["loadexception"]);
13674 this.sortToggle = {};
13675 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13677 Roo.data.Store.superclass.constructor.call(this);
13679 if(this.inlineData){
13680 this.loadData(this.inlineData);
13681 delete this.inlineData;
13685 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13687 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13688 * without a remote query - used by combo/forms at present.
13692 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13695 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13698 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13699 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13702 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13703 * on any HTTP request
13706 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13709 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13713 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13714 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13716 remoteSort : false,
13719 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13720 * loaded or when a record is removed. (defaults to false).
13722 pruneModifiedRecords : false,
13725 lastOptions : null,
13728 * Add Records to the Store and fires the add event.
13729 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13731 add : function(records){
13732 records = [].concat(records);
13733 for(var i = 0, len = records.length; i < len; i++){
13734 records[i].join(this);
13736 var index = this.data.length;
13737 this.data.addAll(records);
13738 this.fireEvent("add", this, records, index);
13742 * Remove a Record from the Store and fires the remove event.
13743 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13745 remove : function(record){
13746 var index = this.data.indexOf(record);
13747 this.data.removeAt(index);
13749 if(this.pruneModifiedRecords){
13750 this.modified.remove(record);
13752 this.fireEvent("remove", this, record, index);
13756 * Remove all Records from the Store and fires the clear event.
13758 removeAll : function(){
13760 if(this.pruneModifiedRecords){
13761 this.modified = [];
13763 this.fireEvent("clear", this);
13767 * Inserts Records to the Store at the given index and fires the add event.
13768 * @param {Number} index The start index at which to insert the passed Records.
13769 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13771 insert : function(index, records){
13772 records = [].concat(records);
13773 for(var i = 0, len = records.length; i < len; i++){
13774 this.data.insert(index, records[i]);
13775 records[i].join(this);
13777 this.fireEvent("add", this, records, index);
13781 * Get the index within the cache of the passed Record.
13782 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13783 * @return {Number} The index of the passed Record. Returns -1 if not found.
13785 indexOf : function(record){
13786 return this.data.indexOf(record);
13790 * Get the index within the cache of the Record with the passed id.
13791 * @param {String} id The id of the Record to find.
13792 * @return {Number} The index of the Record. Returns -1 if not found.
13794 indexOfId : function(id){
13795 return this.data.indexOfKey(id);
13799 * Get the Record with the specified id.
13800 * @param {String} id The id of the Record to find.
13801 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13803 getById : function(id){
13804 return this.data.key(id);
13808 * Get the Record at the specified index.
13809 * @param {Number} index The index of the Record to find.
13810 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13812 getAt : function(index){
13813 return this.data.itemAt(index);
13817 * Returns a range of Records between specified indices.
13818 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13819 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13820 * @return {Roo.data.Record[]} An array of Records
13822 getRange : function(start, end){
13823 return this.data.getRange(start, end);
13827 storeOptions : function(o){
13828 o = Roo.apply({}, o);
13831 this.lastOptions = o;
13835 * Loads the Record cache from the configured Proxy using the configured Reader.
13837 * If using remote paging, then the first load call must specify the <em>start</em>
13838 * and <em>limit</em> properties in the options.params property to establish the initial
13839 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13841 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13842 * and this call will return before the new data has been loaded. Perform any post-processing
13843 * in a callback function, or in a "load" event handler.</strong>
13845 * @param {Object} options An object containing properties which control loading options:<ul>
13846 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13847 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13848 * passed the following arguments:<ul>
13849 * <li>r : Roo.data.Record[]</li>
13850 * <li>options: Options object from the load call</li>
13851 * <li>success: Boolean success indicator</li></ul></li>
13852 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13853 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13856 load : function(options){
13857 options = options || {};
13858 if(this.fireEvent("beforeload", this, options) !== false){
13859 this.storeOptions(options);
13860 var p = Roo.apply(options.params || {}, this.baseParams);
13861 // if meta was not loaded from remote source.. try requesting it.
13862 if (!this.reader.metaFromRemote) {
13863 p._requestMeta = 1;
13865 if(this.sortInfo && this.remoteSort){
13866 var pn = this.paramNames;
13867 p[pn["sort"]] = this.sortInfo.field;
13868 p[pn["dir"]] = this.sortInfo.direction;
13870 if (this.multiSort) {
13871 var pn = this.paramNames;
13872 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13875 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13880 * Reloads the Record cache from the configured Proxy using the configured Reader and
13881 * the options from the last load operation performed.
13882 * @param {Object} options (optional) An object containing properties which may override the options
13883 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13884 * the most recently used options are reused).
13886 reload : function(options){
13887 this.load(Roo.applyIf(options||{}, this.lastOptions));
13891 // Called as a callback by the Reader during a load operation.
13892 loadRecords : function(o, options, success){
13893 if(!o || success === false){
13894 if(success !== false){
13895 this.fireEvent("load", this, [], options, o);
13897 if(options.callback){
13898 options.callback.call(options.scope || this, [], options, false);
13902 // if data returned failure - throw an exception.
13903 if (o.success === false) {
13904 // show a message if no listener is registered.
13905 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13906 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13908 // loadmask wil be hooked into this..
13909 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13912 var r = o.records, t = o.totalRecords || r.length;
13914 this.fireEvent("beforeloadadd", this, r, options, o);
13916 if(!options || options.add !== true){
13917 if(this.pruneModifiedRecords){
13918 this.modified = [];
13920 for(var i = 0, len = r.length; i < len; i++){
13924 this.data = this.snapshot;
13925 delete this.snapshot;
13928 this.data.addAll(r);
13929 this.totalLength = t;
13931 this.fireEvent("datachanged", this);
13933 this.totalLength = Math.max(t, this.data.length+r.length);
13937 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13939 var e = new Roo.data.Record({});
13941 e.set(this.parent.displayField, this.parent.emptyTitle);
13942 e.set(this.parent.valueField, '');
13947 this.fireEvent("load", this, r, options, o);
13948 if(options.callback){
13949 options.callback.call(options.scope || this, r, options, true);
13955 * Loads data from a passed data block. A Reader which understands the format of the data
13956 * must have been configured in the constructor.
13957 * @param {Object} data The data block from which to read the Records. The format of the data expected
13958 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13959 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13961 loadData : function(o, append){
13962 var r = this.reader.readRecords(o);
13963 this.loadRecords(r, {add: append}, true);
13967 * using 'cn' the nested child reader read the child array into it's child stores.
13968 * @param {Object} rec The record with a 'children array
13970 loadDataFromChildren : function(rec)
13972 this.loadData(this.reader.toLoadData(rec));
13977 * Gets the number of cached records.
13979 * <em>If using paging, this may not be the total size of the dataset. If the data object
13980 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13981 * the data set size</em>
13983 getCount : function(){
13984 return this.data.length || 0;
13988 * Gets the total number of records in the dataset as returned by the server.
13990 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13991 * the dataset size</em>
13993 getTotalCount : function(){
13994 return this.totalLength || 0;
13998 * Returns the sort state of the Store as an object with two properties:
14000 field {String} The name of the field by which the Records are sorted
14001 direction {String} The sort order, "ASC" or "DESC"
14004 getSortState : function(){
14005 return this.sortInfo;
14009 applySort : function(){
14010 if(this.sortInfo && !this.remoteSort){
14011 var s = this.sortInfo, f = s.field;
14012 var st = this.fields.get(f).sortType;
14013 var fn = function(r1, r2){
14014 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14015 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14017 this.data.sort(s.direction, fn);
14018 if(this.snapshot && this.snapshot != this.data){
14019 this.snapshot.sort(s.direction, fn);
14025 * Sets the default sort column and order to be used by the next load operation.
14026 * @param {String} fieldName The name of the field to sort by.
14027 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14029 setDefaultSort : function(field, dir){
14030 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14034 * Sort the Records.
14035 * If remote sorting is used, the sort is performed on the server, and the cache is
14036 * reloaded. If local sorting is used, the cache is sorted internally.
14037 * @param {String} fieldName The name of the field to sort by.
14038 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14040 sort : function(fieldName, dir){
14041 var f = this.fields.get(fieldName);
14043 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14045 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14046 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14051 this.sortToggle[f.name] = dir;
14052 this.sortInfo = {field: f.name, direction: dir};
14053 if(!this.remoteSort){
14055 this.fireEvent("datachanged", this);
14057 this.load(this.lastOptions);
14062 * Calls the specified function for each of the Records in the cache.
14063 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14064 * Returning <em>false</em> aborts and exits the iteration.
14065 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14067 each : function(fn, scope){
14068 this.data.each(fn, scope);
14072 * Gets all records modified since the last commit. Modified records are persisted across load operations
14073 * (e.g., during paging).
14074 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14076 getModifiedRecords : function(){
14077 return this.modified;
14081 createFilterFn : function(property, value, anyMatch){
14082 if(!value.exec){ // not a regex
14083 value = String(value);
14084 if(value.length == 0){
14087 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14089 return function(r){
14090 return value.test(r.data[property]);
14095 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14096 * @param {String} property A field on your records
14097 * @param {Number} start The record index to start at (defaults to 0)
14098 * @param {Number} end The last record index to include (defaults to length - 1)
14099 * @return {Number} The sum
14101 sum : function(property, start, end){
14102 var rs = this.data.items, v = 0;
14103 start = start || 0;
14104 end = (end || end === 0) ? end : rs.length-1;
14106 for(var i = start; i <= end; i++){
14107 v += (rs[i].data[property] || 0);
14113 * Filter the records by a specified property.
14114 * @param {String} field A field on your records
14115 * @param {String/RegExp} value Either a string that the field
14116 * should start with or a RegExp to test against the field
14117 * @param {Boolean} anyMatch True to match any part not just the beginning
14119 filter : function(property, value, anyMatch){
14120 var fn = this.createFilterFn(property, value, anyMatch);
14121 return fn ? this.filterBy(fn) : this.clearFilter();
14125 * Filter by a function. The specified function will be called with each
14126 * record in this data source. If the function returns true the record is included,
14127 * otherwise it is filtered.
14128 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14129 * @param {Object} scope (optional) The scope of the function (defaults to this)
14131 filterBy : function(fn, scope){
14132 this.snapshot = this.snapshot || this.data;
14133 this.data = this.queryBy(fn, scope||this);
14134 this.fireEvent("datachanged", this);
14138 * Query the records by a specified property.
14139 * @param {String} field A field on your records
14140 * @param {String/RegExp} value Either a string that the field
14141 * should start with or a RegExp to test against the field
14142 * @param {Boolean} anyMatch True to match any part not just the beginning
14143 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14145 query : function(property, value, anyMatch){
14146 var fn = this.createFilterFn(property, value, anyMatch);
14147 return fn ? this.queryBy(fn) : this.data.clone();
14151 * Query by a function. The specified function will be called with each
14152 * record in this data source. If the function returns true the record is included
14154 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14155 * @param {Object} scope (optional) The scope of the function (defaults to this)
14156 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14158 queryBy : function(fn, scope){
14159 var data = this.snapshot || this.data;
14160 return data.filterBy(fn, scope||this);
14164 * Collects unique values for a particular dataIndex from this store.
14165 * @param {String} dataIndex The property to collect
14166 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14167 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14168 * @return {Array} An array of the unique values
14170 collect : function(dataIndex, allowNull, bypassFilter){
14171 var d = (bypassFilter === true && this.snapshot) ?
14172 this.snapshot.items : this.data.items;
14173 var v, sv, r = [], l = {};
14174 for(var i = 0, len = d.length; i < len; i++){
14175 v = d[i].data[dataIndex];
14177 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14186 * Revert to a view of the Record cache with no filtering applied.
14187 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14189 clearFilter : function(suppressEvent){
14190 if(this.snapshot && this.snapshot != this.data){
14191 this.data = this.snapshot;
14192 delete this.snapshot;
14193 if(suppressEvent !== true){
14194 this.fireEvent("datachanged", this);
14200 afterEdit : function(record){
14201 if(this.modified.indexOf(record) == -1){
14202 this.modified.push(record);
14204 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14208 afterReject : function(record){
14209 this.modified.remove(record);
14210 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14214 afterCommit : function(record){
14215 this.modified.remove(record);
14216 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14220 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14221 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14223 commitChanges : function(){
14224 var m = this.modified.slice(0);
14225 this.modified = [];
14226 for(var i = 0, len = m.length; i < len; i++){
14232 * Cancel outstanding changes on all changed records.
14234 rejectChanges : function(){
14235 var m = this.modified.slice(0);
14236 this.modified = [];
14237 for(var i = 0, len = m.length; i < len; i++){
14242 onMetaChange : function(meta, rtype, o){
14243 this.recordType = rtype;
14244 this.fields = rtype.prototype.fields;
14245 delete this.snapshot;
14246 this.sortInfo = meta.sortInfo || this.sortInfo;
14247 this.modified = [];
14248 this.fireEvent('metachange', this, this.reader.meta);
14251 moveIndex : function(data, type)
14253 var index = this.indexOf(data);
14255 var newIndex = index + type;
14259 this.insert(newIndex, data);
14264 * Ext JS Library 1.1.1
14265 * Copyright(c) 2006-2007, Ext JS, LLC.
14267 * Originally Released Under LGPL - original licence link has changed is not relivant.
14270 * <script type="text/javascript">
14274 * @class Roo.data.SimpleStore
14275 * @extends Roo.data.Store
14276 * Small helper class to make creating Stores from Array data easier.
14277 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14278 * @cfg {Array} fields An array of field definition objects, or field name strings.
14279 * @cfg {Object} an existing reader (eg. copied from another store)
14280 * @cfg {Array} data The multi-dimensional array of data
14282 * @param {Object} config
14284 Roo.data.SimpleStore = function(config)
14286 Roo.data.SimpleStore.superclass.constructor.call(this, {
14288 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14291 Roo.data.Record.create(config.fields)
14293 proxy : new Roo.data.MemoryProxy(config.data)
14297 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14299 * Ext JS Library 1.1.1
14300 * Copyright(c) 2006-2007, Ext JS, LLC.
14302 * Originally Released Under LGPL - original licence link has changed is not relivant.
14305 * <script type="text/javascript">
14310 * @extends Roo.data.Store
14311 * @class Roo.data.JsonStore
14312 * Small helper class to make creating Stores for JSON data easier. <br/>
14314 var store = new Roo.data.JsonStore({
14315 url: 'get-images.php',
14317 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14320 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14321 * JsonReader and HttpProxy (unless inline data is provided).</b>
14322 * @cfg {Array} fields An array of field definition objects, or field name strings.
14324 * @param {Object} config
14326 Roo.data.JsonStore = function(c){
14327 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14328 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14329 reader: new Roo.data.JsonReader(c, c.fields)
14332 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14334 * Ext JS Library 1.1.1
14335 * Copyright(c) 2006-2007, Ext JS, LLC.
14337 * Originally Released Under LGPL - original licence link has changed is not relivant.
14340 * <script type="text/javascript">
14344 Roo.data.Field = function(config){
14345 if(typeof config == "string"){
14346 config = {name: config};
14348 Roo.apply(this, config);
14351 this.type = "auto";
14354 var st = Roo.data.SortTypes;
14355 // named sortTypes are supported, here we look them up
14356 if(typeof this.sortType == "string"){
14357 this.sortType = st[this.sortType];
14360 // set default sortType for strings and dates
14361 if(!this.sortType){
14364 this.sortType = st.asUCString;
14367 this.sortType = st.asDate;
14370 this.sortType = st.none;
14375 var stripRe = /[\$,%]/g;
14377 // prebuilt conversion function for this field, instead of
14378 // switching every time we're reading a value
14380 var cv, dateFormat = this.dateFormat;
14385 cv = function(v){ return v; };
14388 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14392 return v !== undefined && v !== null && v !== '' ?
14393 parseInt(String(v).replace(stripRe, ""), 10) : '';
14398 return v !== undefined && v !== null && v !== '' ?
14399 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14404 cv = function(v){ return v === true || v === "true" || v == 1; };
14411 if(v instanceof Date){
14415 if(dateFormat == "timestamp"){
14416 return new Date(v*1000);
14418 return Date.parseDate(v, dateFormat);
14420 var parsed = Date.parse(v);
14421 return parsed ? new Date(parsed) : null;
14430 Roo.data.Field.prototype = {
14438 * Ext JS Library 1.1.1
14439 * Copyright(c) 2006-2007, Ext JS, LLC.
14441 * Originally Released Under LGPL - original licence link has changed is not relivant.
14444 * <script type="text/javascript">
14447 // Base class for reading structured data from a data source. This class is intended to be
14448 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14451 * @class Roo.data.DataReader
14452 * Base class for reading structured data from a data source. This class is intended to be
14453 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14456 Roo.data.DataReader = function(meta, recordType){
14460 this.recordType = recordType instanceof Array ?
14461 Roo.data.Record.create(recordType) : recordType;
14464 Roo.data.DataReader.prototype = {
14467 readerType : 'Data',
14469 * Create an empty record
14470 * @param {Object} data (optional) - overlay some values
14471 * @return {Roo.data.Record} record created.
14473 newRow : function(d) {
14475 this.recordType.prototype.fields.each(function(c) {
14477 case 'int' : da[c.name] = 0; break;
14478 case 'date' : da[c.name] = new Date(); break;
14479 case 'float' : da[c.name] = 0.0; break;
14480 case 'boolean' : da[c.name] = false; break;
14481 default : da[c.name] = ""; break;
14485 return new this.recordType(Roo.apply(da, d));
14491 * Ext JS Library 1.1.1
14492 * Copyright(c) 2006-2007, Ext JS, LLC.
14494 * Originally Released Under LGPL - original licence link has changed is not relivant.
14497 * <script type="text/javascript">
14501 * @class Roo.data.DataProxy
14502 * @extends Roo.data.Observable
14503 * This class is an abstract base class for implementations which provide retrieval of
14504 * unformatted data objects.<br>
14506 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14507 * (of the appropriate type which knows how to parse the data object) to provide a block of
14508 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14510 * Custom implementations must implement the load method as described in
14511 * {@link Roo.data.HttpProxy#load}.
14513 Roo.data.DataProxy = function(){
14516 * @event beforeload
14517 * Fires before a network request is made to retrieve a data object.
14518 * @param {Object} This DataProxy object.
14519 * @param {Object} params The params parameter to the load function.
14524 * Fires before the load method's callback is called.
14525 * @param {Object} This DataProxy object.
14526 * @param {Object} o The data object.
14527 * @param {Object} arg The callback argument object passed to the load function.
14531 * @event loadexception
14532 * Fires if an Exception occurs during data retrieval.
14533 * @param {Object} This DataProxy object.
14534 * @param {Object} o The data object.
14535 * @param {Object} arg The callback argument object passed to the load function.
14536 * @param {Object} e The Exception.
14538 loadexception : true
14540 Roo.data.DataProxy.superclass.constructor.call(this);
14543 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14546 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14550 * Ext JS Library 1.1.1
14551 * Copyright(c) 2006-2007, Ext JS, LLC.
14553 * Originally Released Under LGPL - original licence link has changed is not relivant.
14556 * <script type="text/javascript">
14559 * @class Roo.data.MemoryProxy
14560 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14561 * to the Reader when its load method is called.
14563 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14565 Roo.data.MemoryProxy = function(data){
14569 Roo.data.MemoryProxy.superclass.constructor.call(this);
14573 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14576 * Load data from the requested source (in this case an in-memory
14577 * data object passed to the constructor), read the data object into
14578 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14579 * process that block using the passed callback.
14580 * @param {Object} params This parameter is not used by the MemoryProxy class.
14581 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14582 * object into a block of Roo.data.Records.
14583 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14584 * The function must be passed <ul>
14585 * <li>The Record block object</li>
14586 * <li>The "arg" argument from the load function</li>
14587 * <li>A boolean success indicator</li>
14589 * @param {Object} scope The scope in which to call the callback
14590 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14592 load : function(params, reader, callback, scope, arg){
14593 params = params || {};
14596 result = reader.readRecords(params.data ? params.data :this.data);
14598 this.fireEvent("loadexception", this, arg, null, e);
14599 callback.call(scope, null, arg, false);
14602 callback.call(scope, result, arg, true);
14606 update : function(params, records){
14611 * Ext JS Library 1.1.1
14612 * Copyright(c) 2006-2007, Ext JS, LLC.
14614 * Originally Released Under LGPL - original licence link has changed is not relivant.
14617 * <script type="text/javascript">
14620 * @class Roo.data.HttpProxy
14621 * @extends Roo.data.DataProxy
14622 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14623 * configured to reference a certain URL.<br><br>
14625 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14626 * from which the running page was served.<br><br>
14628 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14630 * Be aware that to enable the browser to parse an XML document, the server must set
14631 * the Content-Type header in the HTTP response to "text/xml".
14633 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14634 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14635 * will be used to make the request.
14637 Roo.data.HttpProxy = function(conn){
14638 Roo.data.HttpProxy.superclass.constructor.call(this);
14639 // is conn a conn config or a real conn?
14641 this.useAjax = !conn || !conn.events;
14645 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14646 // thse are take from connection...
14649 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14652 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14653 * extra parameters to each request made by this object. (defaults to undefined)
14656 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14657 * to each request made by this object. (defaults to undefined)
14660 * @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)
14663 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14666 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14672 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14676 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14677 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14678 * a finer-grained basis than the DataProxy events.
14680 getConnection : function(){
14681 return this.useAjax ? Roo.Ajax : this.conn;
14685 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14686 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14687 * process that block using the passed callback.
14688 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14689 * for the request to the remote server.
14690 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14691 * object into a block of Roo.data.Records.
14692 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14693 * The function must be passed <ul>
14694 * <li>The Record block object</li>
14695 * <li>The "arg" argument from the load function</li>
14696 * <li>A boolean success indicator</li>
14698 * @param {Object} scope The scope in which to call the callback
14699 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14701 load : function(params, reader, callback, scope, arg){
14702 if(this.fireEvent("beforeload", this, params) !== false){
14704 params : params || {},
14706 callback : callback,
14711 callback : this.loadResponse,
14715 Roo.applyIf(o, this.conn);
14716 if(this.activeRequest){
14717 Roo.Ajax.abort(this.activeRequest);
14719 this.activeRequest = Roo.Ajax.request(o);
14721 this.conn.request(o);
14724 callback.call(scope||this, null, arg, false);
14729 loadResponse : function(o, success, response){
14730 delete this.activeRequest;
14732 this.fireEvent("loadexception", this, o, response);
14733 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14738 result = o.reader.read(response);
14740 this.fireEvent("loadexception", this, o, response, e);
14741 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14745 this.fireEvent("load", this, o, o.request.arg);
14746 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14750 update : function(dataSet){
14755 updateResponse : function(dataSet){
14760 * Ext JS Library 1.1.1
14761 * Copyright(c) 2006-2007, Ext JS, LLC.
14763 * Originally Released Under LGPL - original licence link has changed is not relivant.
14766 * <script type="text/javascript">
14770 * @class Roo.data.ScriptTagProxy
14771 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14772 * other than the originating domain of the running page.<br><br>
14774 * <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
14775 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14777 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14778 * source code that is used as the source inside a <script> tag.<br><br>
14780 * In order for the browser to process the returned data, the server must wrap the data object
14781 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14782 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14783 * depending on whether the callback name was passed:
14786 boolean scriptTag = false;
14787 String cb = request.getParameter("callback");
14790 response.setContentType("text/javascript");
14792 response.setContentType("application/x-json");
14794 Writer out = response.getWriter();
14796 out.write(cb + "(");
14798 out.print(dataBlock.toJsonString());
14805 * @param {Object} config A configuration object.
14807 Roo.data.ScriptTagProxy = function(config){
14808 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14809 Roo.apply(this, config);
14810 this.head = document.getElementsByTagName("head")[0];
14813 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14815 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14817 * @cfg {String} url The URL from which to request the data object.
14820 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14824 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14825 * the server the name of the callback function set up by the load call to process the returned data object.
14826 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14827 * javascript output which calls this named function passing the data object as its only parameter.
14829 callbackParam : "callback",
14831 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14832 * name to the request.
14837 * Load data from the configured URL, read the data object into
14838 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14839 * process that block using the passed callback.
14840 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14841 * for the request to the remote server.
14842 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14843 * object into a block of Roo.data.Records.
14844 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14845 * The function must be passed <ul>
14846 * <li>The Record block object</li>
14847 * <li>The "arg" argument from the load function</li>
14848 * <li>A boolean success indicator</li>
14850 * @param {Object} scope The scope in which to call the callback
14851 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14853 load : function(params, reader, callback, scope, arg){
14854 if(this.fireEvent("beforeload", this, params) !== false){
14856 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14858 var url = this.url;
14859 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14861 url += "&_dc=" + (new Date().getTime());
14863 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14866 cb : "stcCallback"+transId,
14867 scriptId : "stcScript"+transId,
14871 callback : callback,
14877 window[trans.cb] = function(o){
14878 conn.handleResponse(o, trans);
14881 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14883 if(this.autoAbort !== false){
14887 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14889 var script = document.createElement("script");
14890 script.setAttribute("src", url);
14891 script.setAttribute("type", "text/javascript");
14892 script.setAttribute("id", trans.scriptId);
14893 this.head.appendChild(script);
14895 this.trans = trans;
14897 callback.call(scope||this, null, arg, false);
14902 isLoading : function(){
14903 return this.trans ? true : false;
14907 * Abort the current server request.
14909 abort : function(){
14910 if(this.isLoading()){
14911 this.destroyTrans(this.trans);
14916 destroyTrans : function(trans, isLoaded){
14917 this.head.removeChild(document.getElementById(trans.scriptId));
14918 clearTimeout(trans.timeoutId);
14920 window[trans.cb] = undefined;
14922 delete window[trans.cb];
14925 // if hasn't been loaded, wait for load to remove it to prevent script error
14926 window[trans.cb] = function(){
14927 window[trans.cb] = undefined;
14929 delete window[trans.cb];
14936 handleResponse : function(o, trans){
14937 this.trans = false;
14938 this.destroyTrans(trans, true);
14941 result = trans.reader.readRecords(o);
14943 this.fireEvent("loadexception", this, o, trans.arg, e);
14944 trans.callback.call(trans.scope||window, null, trans.arg, false);
14947 this.fireEvent("load", this, o, trans.arg);
14948 trans.callback.call(trans.scope||window, result, trans.arg, true);
14952 handleFailure : function(trans){
14953 this.trans = false;
14954 this.destroyTrans(trans, false);
14955 this.fireEvent("loadexception", this, null, trans.arg);
14956 trans.callback.call(trans.scope||window, null, trans.arg, false);
14960 * Ext JS Library 1.1.1
14961 * Copyright(c) 2006-2007, Ext JS, LLC.
14963 * Originally Released Under LGPL - original licence link has changed is not relivant.
14966 * <script type="text/javascript">
14970 * @class Roo.data.JsonReader
14971 * @extends Roo.data.DataReader
14972 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14973 * based on mappings in a provided Roo.data.Record constructor.
14975 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14976 * in the reply previously.
14981 var RecordDef = Roo.data.Record.create([
14982 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14983 {name: 'occupation'} // This field will use "occupation" as the mapping.
14985 var myReader = new Roo.data.JsonReader({
14986 totalProperty: "results", // The property which contains the total dataset size (optional)
14987 root: "rows", // The property which contains an Array of row objects
14988 id: "id" // The property within each row object that provides an ID for the record (optional)
14992 * This would consume a JSON file like this:
14994 { 'results': 2, 'rows': [
14995 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14996 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14999 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15000 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15001 * paged from the remote server.
15002 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15003 * @cfg {String} root name of the property which contains the Array of row objects.
15004 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15005 * @cfg {Array} fields Array of field definition objects
15007 * Create a new JsonReader
15008 * @param {Object} meta Metadata configuration options
15009 * @param {Object} recordType Either an Array of field definition objects,
15010 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15012 Roo.data.JsonReader = function(meta, recordType){
15015 // set some defaults:
15016 Roo.applyIf(meta, {
15017 totalProperty: 'total',
15018 successProperty : 'success',
15023 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15025 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15027 readerType : 'Json',
15030 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15031 * Used by Store query builder to append _requestMeta to params.
15034 metaFromRemote : false,
15036 * This method is only used by a DataProxy which has retrieved data from a remote server.
15037 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15038 * @return {Object} data A data block which is used by an Roo.data.Store object as
15039 * a cache of Roo.data.Records.
15041 read : function(response){
15042 var json = response.responseText;
15044 var o = /* eval:var:o */ eval("("+json+")");
15046 throw {message: "JsonReader.read: Json object not found"};
15052 this.metaFromRemote = true;
15053 this.meta = o.metaData;
15054 this.recordType = Roo.data.Record.create(o.metaData.fields);
15055 this.onMetaChange(this.meta, this.recordType, o);
15057 return this.readRecords(o);
15060 // private function a store will implement
15061 onMetaChange : function(meta, recordType, o){
15068 simpleAccess: function(obj, subsc) {
15075 getJsonAccessor: function(){
15077 return function(expr) {
15079 return(re.test(expr))
15080 ? new Function("obj", "return obj." + expr)
15085 return Roo.emptyFn;
15090 * Create a data block containing Roo.data.Records from an XML document.
15091 * @param {Object} o An object which contains an Array of row objects in the property specified
15092 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15093 * which contains the total size of the dataset.
15094 * @return {Object} data A data block which is used by an Roo.data.Store object as
15095 * a cache of Roo.data.Records.
15097 readRecords : function(o){
15099 * After any data loads, the raw JSON data is available for further custom processing.
15103 var s = this.meta, Record = this.recordType,
15104 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15106 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15108 if(s.totalProperty) {
15109 this.getTotal = this.getJsonAccessor(s.totalProperty);
15111 if(s.successProperty) {
15112 this.getSuccess = this.getJsonAccessor(s.successProperty);
15114 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15116 var g = this.getJsonAccessor(s.id);
15117 this.getId = function(rec) {
15119 return (r === undefined || r === "") ? null : r;
15122 this.getId = function(){return null;};
15125 for(var jj = 0; jj < fl; jj++){
15127 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15128 this.ef[jj] = this.getJsonAccessor(map);
15132 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15133 if(s.totalProperty){
15134 var vt = parseInt(this.getTotal(o), 10);
15139 if(s.successProperty){
15140 var vs = this.getSuccess(o);
15141 if(vs === false || vs === 'false'){
15146 for(var i = 0; i < c; i++){
15149 var id = this.getId(n);
15150 for(var j = 0; j < fl; j++){
15152 var v = this.ef[j](n);
15154 Roo.log('missing convert for ' + f.name);
15158 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15160 var record = new Record(values, id);
15162 records[i] = record;
15168 totalRecords : totalRecords
15171 // used when loading children.. @see loadDataFromChildren
15172 toLoadData: function(rec)
15174 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15175 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15176 return { data : data, total : data.length };
15181 * Ext JS Library 1.1.1
15182 * Copyright(c) 2006-2007, Ext JS, LLC.
15184 * Originally Released Under LGPL - original licence link has changed is not relivant.
15187 * <script type="text/javascript">
15191 * @class Roo.data.ArrayReader
15192 * @extends Roo.data.DataReader
15193 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15194 * Each element of that Array represents a row of data fields. The
15195 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15196 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15200 var RecordDef = Roo.data.Record.create([
15201 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15202 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15204 var myReader = new Roo.data.ArrayReader({
15205 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15209 * This would consume an Array like this:
15211 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15215 * Create a new JsonReader
15216 * @param {Object} meta Metadata configuration options.
15217 * @param {Object|Array} recordType Either an Array of field definition objects
15219 * @cfg {Array} fields Array of field definition objects
15220 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15221 * as specified to {@link Roo.data.Record#create},
15222 * or an {@link Roo.data.Record} object
15225 * created using {@link Roo.data.Record#create}.
15227 Roo.data.ArrayReader = function(meta, recordType)
15229 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15232 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15235 * Create a data block containing Roo.data.Records from an XML document.
15236 * @param {Object} o An Array of row objects which represents the dataset.
15237 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15238 * a cache of Roo.data.Records.
15240 readRecords : function(o)
15242 var sid = this.meta ? this.meta.id : null;
15243 var recordType = this.recordType, fields = recordType.prototype.fields;
15246 for(var i = 0; i < root.length; i++){
15249 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15250 for(var j = 0, jlen = fields.length; j < jlen; j++){
15251 var f = fields.items[j];
15252 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15253 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15255 values[f.name] = v;
15257 var record = new recordType(values, id);
15259 records[records.length] = record;
15263 totalRecords : records.length
15266 // used when loading children.. @see loadDataFromChildren
15267 toLoadData: function(rec)
15269 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15270 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15281 * @class Roo.bootstrap.ComboBox
15282 * @extends Roo.bootstrap.TriggerField
15283 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15284 * @cfg {Boolean} append (true|false) default false
15285 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15286 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15287 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15288 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15289 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15290 * @cfg {Boolean} animate default true
15291 * @cfg {Boolean} emptyResultText only for touch device
15292 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15293 * @cfg {String} emptyTitle default ''
15294 * @cfg {Number} width fixed with? experimental
15296 * Create a new ComboBox.
15297 * @param {Object} config Configuration options
15299 Roo.bootstrap.ComboBox = function(config){
15300 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15304 * Fires when the dropdown list is expanded
15305 * @param {Roo.bootstrap.ComboBox} combo This combo box
15310 * Fires when the dropdown list is collapsed
15311 * @param {Roo.bootstrap.ComboBox} combo This combo box
15315 * @event beforeselect
15316 * Fires before a list item is selected. Return false to cancel the selection.
15317 * @param {Roo.bootstrap.ComboBox} combo This combo box
15318 * @param {Roo.data.Record} record The data record returned from the underlying store
15319 * @param {Number} index The index of the selected item in the dropdown list
15321 'beforeselect' : true,
15324 * Fires when a list item is selected
15325 * @param {Roo.bootstrap.ComboBox} combo This combo box
15326 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15327 * @param {Number} index The index of the selected item in the dropdown list
15331 * @event beforequery
15332 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15333 * The event object passed has these properties:
15334 * @param {Roo.bootstrap.ComboBox} combo This combo box
15335 * @param {String} query The query
15336 * @param {Boolean} forceAll true to force "all" query
15337 * @param {Boolean} cancel true to cancel the query
15338 * @param {Object} e The query event object
15340 'beforequery': true,
15343 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15344 * @param {Roo.bootstrap.ComboBox} combo This combo box
15349 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15350 * @param {Roo.bootstrap.ComboBox} combo This combo box
15351 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15356 * Fires when the remove value from the combobox array
15357 * @param {Roo.bootstrap.ComboBox} combo This combo box
15361 * @event afterremove
15362 * Fires when the remove value from the combobox array
15363 * @param {Roo.bootstrap.ComboBox} combo This combo box
15365 'afterremove' : true,
15367 * @event specialfilter
15368 * Fires when specialfilter
15369 * @param {Roo.bootstrap.ComboBox} combo This combo box
15371 'specialfilter' : true,
15374 * Fires when tick the element
15375 * @param {Roo.bootstrap.ComboBox} combo This combo box
15379 * @event touchviewdisplay
15380 * Fires when touch view require special display (default is using displayField)
15381 * @param {Roo.bootstrap.ComboBox} combo This combo box
15382 * @param {Object} cfg set html .
15384 'touchviewdisplay' : true
15389 this.tickItems = [];
15391 this.selectedIndex = -1;
15392 if(this.mode == 'local'){
15393 if(config.queryDelay === undefined){
15394 this.queryDelay = 10;
15396 if(config.minChars === undefined){
15402 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15405 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15406 * rendering into an Roo.Editor, defaults to false)
15409 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15410 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15413 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15416 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15417 * the dropdown list (defaults to undefined, with no header element)
15421 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15425 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15427 listWidth: undefined,
15429 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15430 * mode = 'remote' or 'text' if mode = 'local')
15432 displayField: undefined,
15435 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15436 * mode = 'remote' or 'value' if mode = 'local').
15437 * Note: use of a valueField requires the user make a selection
15438 * in order for a value to be mapped.
15440 valueField: undefined,
15442 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15447 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15448 * field's data value (defaults to the underlying DOM element's name)
15450 hiddenName: undefined,
15452 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15456 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15458 selectedClass: 'active',
15461 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15465 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15466 * anchor positions (defaults to 'tl-bl')
15468 listAlign: 'tl-bl?',
15470 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15474 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15475 * query specified by the allQuery config option (defaults to 'query')
15477 triggerAction: 'query',
15479 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15480 * (defaults to 4, does not apply if editable = false)
15484 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15485 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15489 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15490 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15494 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15495 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15499 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15500 * when editable = true (defaults to false)
15502 selectOnFocus:false,
15504 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15506 queryParam: 'query',
15508 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15509 * when mode = 'remote' (defaults to 'Loading...')
15511 loadingText: 'Loading...',
15513 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15517 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15521 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15522 * traditional select (defaults to true)
15526 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15530 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15534 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15535 * listWidth has a higher value)
15539 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15540 * allow the user to set arbitrary text into the field (defaults to false)
15542 forceSelection:false,
15544 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15545 * if typeAhead = true (defaults to 250)
15547 typeAheadDelay : 250,
15549 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15550 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15552 valueNotFoundText : undefined,
15554 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15556 blockFocus : false,
15559 * @cfg {Boolean} disableClear Disable showing of clear button.
15561 disableClear : false,
15563 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15565 alwaysQuery : false,
15568 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15573 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15575 invalidClass : "has-warning",
15578 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15580 validClass : "has-success",
15583 * @cfg {Boolean} specialFilter (true|false) special filter default false
15585 specialFilter : false,
15588 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15590 mobileTouchView : true,
15593 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15595 useNativeIOS : false,
15598 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15600 mobile_restrict_height : false,
15602 ios_options : false,
15614 btnPosition : 'right',
15615 triggerList : true,
15616 showToggleBtn : true,
15618 emptyResultText: 'Empty',
15619 triggerText : 'Select',
15623 // element that contains real text value.. (when hidden is used..)
15625 getAutoCreate : function()
15630 * Render classic select for iso
15633 if(Roo.isIOS && this.useNativeIOS){
15634 cfg = this.getAutoCreateNativeIOS();
15642 if(Roo.isTouch && this.mobileTouchView){
15643 cfg = this.getAutoCreateTouchView();
15650 if(!this.tickable){
15651 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15656 * ComboBox with tickable selections
15659 var align = this.labelAlign || this.parentLabelAlign();
15662 cls : 'form-group roo-combobox-tickable' //input-group
15665 var btn_text_select = '';
15666 var btn_text_done = '';
15667 var btn_text_cancel = '';
15669 if (this.btn_text_show) {
15670 btn_text_select = 'Select';
15671 btn_text_done = 'Done';
15672 btn_text_cancel = 'Cancel';
15677 cls : 'tickable-buttons',
15682 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15683 //html : this.triggerText
15684 html: btn_text_select
15690 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15692 html: btn_text_done
15698 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15700 html: btn_text_cancel
15706 buttons.cn.unshift({
15708 cls: 'roo-select2-search-field-input'
15714 Roo.each(buttons.cn, function(c){
15716 c.cls += ' btn-' + _this.size;
15719 if (_this.disabled) {
15726 style : 'display: contents',
15731 cls: 'form-hidden-field'
15735 cls: 'roo-select2-choices',
15739 cls: 'roo-select2-search-field',
15750 cls: 'roo-select2-container input-group roo-select2-container-multi',
15756 // cls: 'typeahead typeahead-long dropdown-menu',
15757 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15762 if(this.hasFeedback && !this.allowBlank){
15766 cls: 'glyphicon form-control-feedback'
15769 combobox.cn.push(feedback);
15776 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15777 tooltip : 'This field is required'
15779 if (Roo.bootstrap.version == 4) {
15782 style : 'display:none'
15785 if (align ==='left' && this.fieldLabel.length) {
15787 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15794 cls : 'control-label col-form-label',
15795 html : this.fieldLabel
15807 var labelCfg = cfg.cn[1];
15808 var contentCfg = cfg.cn[2];
15811 if(this.indicatorpos == 'right'){
15817 cls : 'control-label col-form-label',
15821 html : this.fieldLabel
15837 labelCfg = cfg.cn[0];
15838 contentCfg = cfg.cn[1];
15842 if(this.labelWidth > 12){
15843 labelCfg.style = "width: " + this.labelWidth + 'px';
15845 if(this.width * 1 > 0){
15846 contentCfg.style = "width: " + this.width + 'px';
15848 if(this.labelWidth < 13 && this.labelmd == 0){
15849 this.labelmd = this.labelWidth;
15852 if(this.labellg > 0){
15853 labelCfg.cls += ' col-lg-' + this.labellg;
15854 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15857 if(this.labelmd > 0){
15858 labelCfg.cls += ' col-md-' + this.labelmd;
15859 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15862 if(this.labelsm > 0){
15863 labelCfg.cls += ' col-sm-' + this.labelsm;
15864 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15867 if(this.labelxs > 0){
15868 labelCfg.cls += ' col-xs-' + this.labelxs;
15869 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15873 } else if ( this.fieldLabel.length) {
15874 // Roo.log(" label");
15879 //cls : 'input-group-addon',
15880 html : this.fieldLabel
15885 if(this.indicatorpos == 'right'){
15889 //cls : 'input-group-addon',
15890 html : this.fieldLabel
15900 // Roo.log(" no label && no align");
15907 ['xs','sm','md','lg'].map(function(size){
15908 if (settings[size]) {
15909 cfg.cls += ' col-' + size + '-' + settings[size];
15917 _initEventsCalled : false,
15920 initEvents: function()
15922 if (this._initEventsCalled) { // as we call render... prevent looping...
15925 this._initEventsCalled = true;
15928 throw "can not find store for combo";
15931 this.indicator = this.indicatorEl();
15933 this.store = Roo.factory(this.store, Roo.data);
15934 this.store.parent = this;
15936 // if we are building from html. then this element is so complex, that we can not really
15937 // use the rendered HTML.
15938 // so we have to trash and replace the previous code.
15939 if (Roo.XComponent.build_from_html) {
15940 // remove this element....
15941 var e = this.el.dom, k=0;
15942 while (e ) { e = e.previousSibling; ++k;}
15947 this.rendered = false;
15949 this.render(this.parent().getChildContainer(true), k);
15952 if(Roo.isIOS && this.useNativeIOS){
15953 this.initIOSView();
15961 if(Roo.isTouch && this.mobileTouchView){
15962 this.initTouchView();
15967 this.initTickableEvents();
15971 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15973 if(this.hiddenName){
15975 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15977 this.hiddenField.dom.value =
15978 this.hiddenValue !== undefined ? this.hiddenValue :
15979 this.value !== undefined ? this.value : '';
15981 // prevent input submission
15982 this.el.dom.removeAttribute('name');
15983 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15988 // this.el.dom.setAttribute('autocomplete', 'off');
15991 var cls = 'x-combo-list';
15993 //this.list = new Roo.Layer({
15994 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16000 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16001 _this.list.setWidth(lw);
16004 this.list.on('mouseover', this.onViewOver, this);
16005 this.list.on('mousemove', this.onViewMove, this);
16006 this.list.on('scroll', this.onViewScroll, this);
16009 this.list.swallowEvent('mousewheel');
16010 this.assetHeight = 0;
16013 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16014 this.assetHeight += this.header.getHeight();
16017 this.innerList = this.list.createChild({cls:cls+'-inner'});
16018 this.innerList.on('mouseover', this.onViewOver, this);
16019 this.innerList.on('mousemove', this.onViewMove, this);
16020 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16022 if(this.allowBlank && !this.pageSize && !this.disableClear){
16023 this.footer = this.list.createChild({cls:cls+'-ft'});
16024 this.pageTb = new Roo.Toolbar(this.footer);
16028 this.footer = this.list.createChild({cls:cls+'-ft'});
16029 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16030 {pageSize: this.pageSize});
16034 if (this.pageTb && this.allowBlank && !this.disableClear) {
16036 this.pageTb.add(new Roo.Toolbar.Fill(), {
16037 cls: 'x-btn-icon x-btn-clear',
16039 handler: function()
16042 _this.clearValue();
16043 _this.onSelect(false, -1);
16048 this.assetHeight += this.footer.getHeight();
16053 this.tpl = Roo.bootstrap.version == 4 ?
16054 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16055 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16058 this.view = new Roo.View(this.list, this.tpl, {
16059 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16061 //this.view.wrapEl.setDisplayed(false);
16062 this.view.on('click', this.onViewClick, this);
16065 this.store.on('beforeload', this.onBeforeLoad, this);
16066 this.store.on('load', this.onLoad, this);
16067 this.store.on('loadexception', this.onLoadException, this);
16069 if(this.resizable){
16070 this.resizer = new Roo.Resizable(this.list, {
16071 pinned:true, handles:'se'
16073 this.resizer.on('resize', function(r, w, h){
16074 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16075 this.listWidth = w;
16076 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16077 this.restrictHeight();
16079 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16082 if(!this.editable){
16083 this.editable = true;
16084 this.setEditable(false);
16089 if (typeof(this.events.add.listeners) != 'undefined') {
16091 this.addicon = this.wrap.createChild(
16092 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16094 this.addicon.on('click', function(e) {
16095 this.fireEvent('add', this);
16098 if (typeof(this.events.edit.listeners) != 'undefined') {
16100 this.editicon = this.wrap.createChild(
16101 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16102 if (this.addicon) {
16103 this.editicon.setStyle('margin-left', '40px');
16105 this.editicon.on('click', function(e) {
16107 // we fire even if inothing is selected..
16108 this.fireEvent('edit', this, this.lastData );
16114 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16115 "up" : function(e){
16116 this.inKeyMode = true;
16120 "down" : function(e){
16121 if(!this.isExpanded()){
16122 this.onTriggerClick();
16124 this.inKeyMode = true;
16129 "enter" : function(e){
16130 // this.onViewClick();
16134 if(this.fireEvent("specialkey", this, e)){
16135 this.onViewClick(false);
16141 "esc" : function(e){
16145 "tab" : function(e){
16148 if(this.fireEvent("specialkey", this, e)){
16149 this.onViewClick(false);
16157 doRelay : function(foo, bar, hname){
16158 if(hname == 'down' || this.scope.isExpanded()){
16159 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16168 this.queryDelay = Math.max(this.queryDelay || 10,
16169 this.mode == 'local' ? 10 : 250);
16172 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16174 if(this.typeAhead){
16175 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16177 if(this.editable !== false){
16178 this.inputEl().on("keyup", this.onKeyUp, this);
16180 if(this.forceSelection){
16181 this.inputEl().on('blur', this.doForce, this);
16185 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16186 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16190 initTickableEvents: function()
16194 if(this.hiddenName){
16196 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16198 this.hiddenField.dom.value =
16199 this.hiddenValue !== undefined ? this.hiddenValue :
16200 this.value !== undefined ? this.value : '';
16202 // prevent input submission
16203 this.el.dom.removeAttribute('name');
16204 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16209 // this.list = this.el.select('ul.dropdown-menu',true).first();
16211 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16212 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16213 if(this.triggerList){
16214 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16217 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16218 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16220 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16221 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16223 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16224 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16226 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16227 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16228 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16231 this.cancelBtn.hide();
16236 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16237 _this.list.setWidth(lw);
16240 this.list.on('mouseover', this.onViewOver, this);
16241 this.list.on('mousemove', this.onViewMove, this);
16243 this.list.on('scroll', this.onViewScroll, this);
16246 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16247 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16250 this.view = new Roo.View(this.list, this.tpl, {
16255 selectedClass: this.selectedClass
16258 //this.view.wrapEl.setDisplayed(false);
16259 this.view.on('click', this.onViewClick, this);
16263 this.store.on('beforeload', this.onBeforeLoad, this);
16264 this.store.on('load', this.onLoad, this);
16265 this.store.on('loadexception', this.onLoadException, this);
16268 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16269 "up" : function(e){
16270 this.inKeyMode = true;
16274 "down" : function(e){
16275 this.inKeyMode = true;
16279 "enter" : function(e){
16280 if(this.fireEvent("specialkey", this, e)){
16281 this.onViewClick(false);
16287 "esc" : function(e){
16288 this.onTickableFooterButtonClick(e, false, false);
16291 "tab" : function(e){
16292 this.fireEvent("specialkey", this, e);
16294 this.onTickableFooterButtonClick(e, false, false);
16301 doRelay : function(e, fn, key){
16302 if(this.scope.isExpanded()){
16303 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16312 this.queryDelay = Math.max(this.queryDelay || 10,
16313 this.mode == 'local' ? 10 : 250);
16316 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16318 if(this.typeAhead){
16319 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16322 if(this.editable !== false){
16323 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16326 this.indicator = this.indicatorEl();
16328 if(this.indicator){
16329 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16330 this.indicator.hide();
16335 onDestroy : function(){
16337 this.view.setStore(null);
16338 this.view.el.removeAllListeners();
16339 this.view.el.remove();
16340 this.view.purgeListeners();
16343 this.list.dom.innerHTML = '';
16347 this.store.un('beforeload', this.onBeforeLoad, this);
16348 this.store.un('load', this.onLoad, this);
16349 this.store.un('loadexception', this.onLoadException, this);
16351 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16355 fireKey : function(e){
16356 if(e.isNavKeyPress() && !this.list.isVisible()){
16357 this.fireEvent("specialkey", this, e);
16362 onResize: function(w, h)
16366 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16368 // if(typeof w != 'number'){
16369 // // we do not handle it!?!?
16372 // var tw = this.trigger.getWidth();
16373 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16374 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16376 // this.inputEl().setWidth( this.adjustWidth('input', x));
16378 // //this.trigger.setStyle('left', x+'px');
16380 // if(this.list && this.listWidth === undefined){
16381 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16382 // this.list.setWidth(lw);
16383 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16391 * Allow or prevent the user from directly editing the field text. If false is passed,
16392 * the user will only be able to select from the items defined in the dropdown list. This method
16393 * is the runtime equivalent of setting the 'editable' config option at config time.
16394 * @param {Boolean} value True to allow the user to directly edit the field text
16396 setEditable : function(value){
16397 if(value == this.editable){
16400 this.editable = value;
16402 this.inputEl().dom.setAttribute('readOnly', true);
16403 this.inputEl().on('mousedown', this.onTriggerClick, this);
16404 this.inputEl().addClass('x-combo-noedit');
16406 this.inputEl().dom.removeAttribute('readOnly');
16407 this.inputEl().un('mousedown', this.onTriggerClick, this);
16408 this.inputEl().removeClass('x-combo-noedit');
16414 onBeforeLoad : function(combo,opts){
16415 if(!this.hasFocus){
16419 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16421 this.restrictHeight();
16422 this.selectedIndex = -1;
16426 onLoad : function(){
16428 this.hasQuery = false;
16430 if(!this.hasFocus){
16434 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16435 this.loading.hide();
16438 if(this.store.getCount() > 0){
16441 this.restrictHeight();
16442 if(this.lastQuery == this.allQuery){
16443 if(this.editable && !this.tickable){
16444 this.inputEl().dom.select();
16448 !this.selectByValue(this.value, true) &&
16451 !this.store.lastOptions ||
16452 typeof(this.store.lastOptions.add) == 'undefined' ||
16453 this.store.lastOptions.add != true
16456 this.select(0, true);
16459 if(this.autoFocus){
16462 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16463 this.taTask.delay(this.typeAheadDelay);
16467 this.onEmptyResults();
16473 onLoadException : function()
16475 this.hasQuery = false;
16477 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16478 this.loading.hide();
16481 if(this.tickable && this.editable){
16486 // only causes errors at present
16487 //Roo.log(this.store.reader.jsonData);
16488 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16490 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16496 onTypeAhead : function(){
16497 if(this.store.getCount() > 0){
16498 var r = this.store.getAt(0);
16499 var newValue = r.data[this.displayField];
16500 var len = newValue.length;
16501 var selStart = this.getRawValue().length;
16503 if(selStart != len){
16504 this.setRawValue(newValue);
16505 this.selectText(selStart, newValue.length);
16511 onSelect : function(record, index){
16513 if(this.fireEvent('beforeselect', this, record, index) !== false){
16515 this.setFromData(index > -1 ? record.data : false);
16518 this.fireEvent('select', this, record, index);
16523 * Returns the currently selected field value or empty string if no value is set.
16524 * @return {String} value The selected value
16526 getValue : function()
16528 if(Roo.isIOS && this.useNativeIOS){
16529 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16533 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16536 if(this.valueField){
16537 return typeof this.value != 'undefined' ? this.value : '';
16539 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16543 getRawValue : function()
16545 if(Roo.isIOS && this.useNativeIOS){
16546 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16549 var v = this.inputEl().getValue();
16555 * Clears any text/value currently set in the field
16557 clearValue : function(){
16559 if(this.hiddenField){
16560 this.hiddenField.dom.value = '';
16563 this.setRawValue('');
16564 this.lastSelectionText = '';
16565 this.lastData = false;
16567 var close = this.closeTriggerEl();
16578 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16579 * will be displayed in the field. If the value does not match the data value of an existing item,
16580 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16581 * Otherwise the field will be blank (although the value will still be set).
16582 * @param {String} value The value to match
16584 setValue : function(v)
16586 if(Roo.isIOS && this.useNativeIOS){
16587 this.setIOSValue(v);
16597 if(this.valueField){
16598 var r = this.findRecord(this.valueField, v);
16600 text = r.data[this.displayField];
16601 }else if(this.valueNotFoundText !== undefined){
16602 text = this.valueNotFoundText;
16605 this.lastSelectionText = text;
16606 if(this.hiddenField){
16607 this.hiddenField.dom.value = v;
16609 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16612 var close = this.closeTriggerEl();
16615 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16621 * @property {Object} the last set data for the element
16626 * Sets the value of the field based on a object which is related to the record format for the store.
16627 * @param {Object} value the value to set as. or false on reset?
16629 setFromData : function(o){
16636 var dv = ''; // display value
16637 var vv = ''; // value value..
16639 if (this.displayField) {
16640 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16642 // this is an error condition!!!
16643 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16646 if(this.valueField){
16647 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16650 var close = this.closeTriggerEl();
16653 if(dv.length || vv * 1 > 0){
16655 this.blockFocus=true;
16661 if(this.hiddenField){
16662 this.hiddenField.dom.value = vv;
16664 this.lastSelectionText = dv;
16665 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16669 // no hidden field.. - we store the value in 'value', but still display
16670 // display field!!!!
16671 this.lastSelectionText = dv;
16672 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16679 reset : function(){
16680 // overridden so that last data is reset..
16687 this.setValue(this.originalValue);
16688 //this.clearInvalid();
16689 this.lastData = false;
16691 this.view.clearSelections();
16697 findRecord : function(prop, value){
16699 if(this.store.getCount() > 0){
16700 this.store.each(function(r){
16701 if(r.data[prop] == value){
16711 getName: function()
16713 // returns hidden if it's set..
16714 if (!this.rendered) {return ''};
16715 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16719 onViewMove : function(e, t){
16720 this.inKeyMode = false;
16724 onViewOver : function(e, t){
16725 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16728 var item = this.view.findItemFromChild(t);
16731 var index = this.view.indexOf(item);
16732 this.select(index, false);
16737 onViewClick : function(view, doFocus, el, e)
16739 var index = this.view.getSelectedIndexes()[0];
16741 var r = this.store.getAt(index);
16745 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16752 Roo.each(this.tickItems, function(v,k){
16754 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16756 _this.tickItems.splice(k, 1);
16758 if(typeof(e) == 'undefined' && view == false){
16759 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16771 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16772 this.tickItems.push(r.data);
16775 if(typeof(e) == 'undefined' && view == false){
16776 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16783 this.onSelect(r, index);
16785 if(doFocus !== false && !this.blockFocus){
16786 this.inputEl().focus();
16791 restrictHeight : function(){
16792 //this.innerList.dom.style.height = '';
16793 //var inner = this.innerList.dom;
16794 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16795 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16796 //this.list.beginUpdate();
16797 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16798 this.list.alignTo(this.inputEl(), this.listAlign);
16799 this.list.alignTo(this.inputEl(), this.listAlign);
16800 //this.list.endUpdate();
16804 onEmptyResults : function(){
16806 if(this.tickable && this.editable){
16807 this.hasFocus = false;
16808 this.restrictHeight();
16816 * Returns true if the dropdown list is expanded, else false.
16818 isExpanded : function(){
16819 return this.list.isVisible();
16823 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16824 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16825 * @param {String} value The data value of the item to select
16826 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16827 * selected item if it is not currently in view (defaults to true)
16828 * @return {Boolean} True if the value matched an item in the list, else false
16830 selectByValue : function(v, scrollIntoView){
16831 if(v !== undefined && v !== null){
16832 var r = this.findRecord(this.valueField || this.displayField, v);
16834 this.select(this.store.indexOf(r), scrollIntoView);
16842 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16843 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16844 * @param {Number} index The zero-based index of the list item to select
16845 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16846 * selected item if it is not currently in view (defaults to true)
16848 select : function(index, scrollIntoView){
16849 this.selectedIndex = index;
16850 this.view.select(index);
16851 if(scrollIntoView !== false){
16852 var el = this.view.getNode(index);
16854 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16857 this.list.scrollChildIntoView(el, false);
16863 selectNext : function(){
16864 var ct = this.store.getCount();
16866 if(this.selectedIndex == -1){
16868 }else if(this.selectedIndex < ct-1){
16869 this.select(this.selectedIndex+1);
16875 selectPrev : function(){
16876 var ct = this.store.getCount();
16878 if(this.selectedIndex == -1){
16880 }else if(this.selectedIndex != 0){
16881 this.select(this.selectedIndex-1);
16887 onKeyUp : function(e){
16888 if(this.editable !== false && !e.isSpecialKey()){
16889 this.lastKey = e.getKey();
16890 this.dqTask.delay(this.queryDelay);
16895 validateBlur : function(){
16896 return !this.list || !this.list.isVisible();
16900 initQuery : function(){
16902 var v = this.getRawValue();
16904 if(this.tickable && this.editable){
16905 v = this.tickableInputEl().getValue();
16912 doForce : function(){
16913 if(this.inputEl().dom.value.length > 0){
16914 this.inputEl().dom.value =
16915 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16921 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16922 * query allowing the query action to be canceled if needed.
16923 * @param {String} query The SQL query to execute
16924 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16925 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16926 * saved in the current store (defaults to false)
16928 doQuery : function(q, forceAll){
16930 if(q === undefined || q === null){
16935 forceAll: forceAll,
16939 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16944 forceAll = qe.forceAll;
16945 if(forceAll === true || (q.length >= this.minChars)){
16947 this.hasQuery = true;
16949 if(this.lastQuery != q || this.alwaysQuery){
16950 this.lastQuery = q;
16951 if(this.mode == 'local'){
16952 this.selectedIndex = -1;
16954 this.store.clearFilter();
16957 if(this.specialFilter){
16958 this.fireEvent('specialfilter', this);
16963 this.store.filter(this.displayField, q);
16966 this.store.fireEvent("datachanged", this.store);
16973 this.store.baseParams[this.queryParam] = q;
16975 var options = {params : this.getParams(q)};
16978 options.add = true;
16979 options.params.start = this.page * this.pageSize;
16982 this.store.load(options);
16985 * this code will make the page width larger, at the beginning, the list not align correctly,
16986 * we should expand the list on onLoad
16987 * so command out it
16992 this.selectedIndex = -1;
16997 this.loadNext = false;
17001 getParams : function(q){
17003 //p[this.queryParam] = q;
17007 p.limit = this.pageSize;
17013 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17015 collapse : function(){
17016 if(!this.isExpanded()){
17022 this.hasFocus = false;
17026 this.cancelBtn.hide();
17027 this.trigger.show();
17030 this.tickableInputEl().dom.value = '';
17031 this.tickableInputEl().blur();
17036 Roo.get(document).un('mousedown', this.collapseIf, this);
17037 Roo.get(document).un('mousewheel', this.collapseIf, this);
17038 if (!this.editable) {
17039 Roo.get(document).un('keydown', this.listKeyPress, this);
17041 this.fireEvent('collapse', this);
17047 collapseIf : function(e){
17048 var in_combo = e.within(this.el);
17049 var in_list = e.within(this.list);
17050 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17052 if (in_combo || in_list || is_list) {
17053 //e.stopPropagation();
17058 this.onTickableFooterButtonClick(e, false, false);
17066 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17068 expand : function(){
17070 if(this.isExpanded() || !this.hasFocus){
17074 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17075 this.list.setWidth(lw);
17081 this.restrictHeight();
17085 this.tickItems = Roo.apply([], this.item);
17088 this.cancelBtn.show();
17089 this.trigger.hide();
17092 this.tickableInputEl().focus();
17097 Roo.get(document).on('mousedown', this.collapseIf, this);
17098 Roo.get(document).on('mousewheel', this.collapseIf, this);
17099 if (!this.editable) {
17100 Roo.get(document).on('keydown', this.listKeyPress, this);
17103 this.fireEvent('expand', this);
17107 // Implements the default empty TriggerField.onTriggerClick function
17108 onTriggerClick : function(e)
17110 Roo.log('trigger click');
17112 if(this.disabled || !this.triggerList){
17117 this.loadNext = false;
17119 if(this.isExpanded()){
17121 if (!this.blockFocus) {
17122 this.inputEl().focus();
17126 this.hasFocus = true;
17127 if(this.triggerAction == 'all') {
17128 this.doQuery(this.allQuery, true);
17130 this.doQuery(this.getRawValue());
17132 if (!this.blockFocus) {
17133 this.inputEl().focus();
17138 onTickableTriggerClick : function(e)
17145 this.loadNext = false;
17146 this.hasFocus = true;
17148 if(this.triggerAction == 'all') {
17149 this.doQuery(this.allQuery, true);
17151 this.doQuery(this.getRawValue());
17155 onSearchFieldClick : function(e)
17157 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17158 this.onTickableFooterButtonClick(e, false, false);
17162 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17167 this.loadNext = false;
17168 this.hasFocus = true;
17170 if(this.triggerAction == 'all') {
17171 this.doQuery(this.allQuery, true);
17173 this.doQuery(this.getRawValue());
17177 listKeyPress : function(e)
17179 //Roo.log('listkeypress');
17180 // scroll to first matching element based on key pres..
17181 if (e.isSpecialKey()) {
17184 var k = String.fromCharCode(e.getKey()).toUpperCase();
17187 var csel = this.view.getSelectedNodes();
17188 var cselitem = false;
17190 var ix = this.view.indexOf(csel[0]);
17191 cselitem = this.store.getAt(ix);
17192 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17198 this.store.each(function(v) {
17200 // start at existing selection.
17201 if (cselitem.id == v.id) {
17207 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17208 match = this.store.indexOf(v);
17214 if (match === false) {
17215 return true; // no more action?
17218 this.view.select(match);
17219 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17220 sn.scrollIntoView(sn.dom.parentNode, false);
17223 onViewScroll : function(e, t){
17225 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){
17229 this.hasQuery = true;
17231 this.loading = this.list.select('.loading', true).first();
17233 if(this.loading === null){
17234 this.list.createChild({
17236 cls: 'loading roo-select2-more-results roo-select2-active',
17237 html: 'Loading more results...'
17240 this.loading = this.list.select('.loading', true).first();
17242 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17244 this.loading.hide();
17247 this.loading.show();
17252 this.loadNext = true;
17254 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17259 addItem : function(o)
17261 var dv = ''; // display value
17263 if (this.displayField) {
17264 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17266 // this is an error condition!!!
17267 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17274 var choice = this.choices.createChild({
17276 cls: 'roo-select2-search-choice',
17285 cls: 'roo-select2-search-choice-close fa fa-times',
17290 }, this.searchField);
17292 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17294 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17302 this.inputEl().dom.value = '';
17307 onRemoveItem : function(e, _self, o)
17309 e.preventDefault();
17311 this.lastItem = Roo.apply([], this.item);
17313 var index = this.item.indexOf(o.data) * 1;
17316 Roo.log('not this item?!');
17320 this.item.splice(index, 1);
17325 this.fireEvent('remove', this, e);
17331 syncValue : function()
17333 if(!this.item.length){
17340 Roo.each(this.item, function(i){
17341 if(_this.valueField){
17342 value.push(i[_this.valueField]);
17349 this.value = value.join(',');
17351 if(this.hiddenField){
17352 this.hiddenField.dom.value = this.value;
17355 this.store.fireEvent("datachanged", this.store);
17360 clearItem : function()
17362 if(!this.multiple){
17368 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17376 if(this.tickable && !Roo.isTouch){
17377 this.view.refresh();
17381 inputEl: function ()
17383 if(Roo.isIOS && this.useNativeIOS){
17384 return this.el.select('select.roo-ios-select', true).first();
17387 if(Roo.isTouch && this.mobileTouchView){
17388 return this.el.select('input.form-control',true).first();
17392 return this.searchField;
17395 return this.el.select('input.form-control',true).first();
17398 onTickableFooterButtonClick : function(e, btn, el)
17400 e.preventDefault();
17402 this.lastItem = Roo.apply([], this.item);
17404 if(btn && btn.name == 'cancel'){
17405 this.tickItems = Roo.apply([], this.item);
17414 Roo.each(this.tickItems, function(o){
17422 validate : function()
17424 if(this.getVisibilityEl().hasClass('hidden')){
17428 var v = this.getRawValue();
17431 v = this.getValue();
17434 if(this.disabled || this.allowBlank || v.length){
17439 this.markInvalid();
17443 tickableInputEl : function()
17445 if(!this.tickable || !this.editable){
17446 return this.inputEl();
17449 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17453 getAutoCreateTouchView : function()
17458 cls: 'form-group' //input-group
17464 type : this.inputType,
17465 cls : 'form-control x-combo-noedit',
17466 autocomplete: 'new-password',
17467 placeholder : this.placeholder || '',
17472 input.name = this.name;
17476 input.cls += ' input-' + this.size;
17479 if (this.disabled) {
17480 input.disabled = true;
17484 cls : 'roo-combobox-wrap',
17491 inputblock.cls += ' input-group';
17493 inputblock.cn.unshift({
17495 cls : 'input-group-addon input-group-prepend input-group-text',
17500 if(this.removable && !this.multiple){
17501 inputblock.cls += ' roo-removable';
17503 inputblock.cn.push({
17506 cls : 'roo-combo-removable-btn close'
17510 if(this.hasFeedback && !this.allowBlank){
17512 inputblock.cls += ' has-feedback';
17514 inputblock.cn.push({
17516 cls: 'glyphicon form-control-feedback'
17523 inputblock.cls += (this.before) ? '' : ' input-group';
17525 inputblock.cn.push({
17527 cls : 'input-group-addon input-group-append input-group-text',
17533 var ibwrap = inputblock;
17538 cls: 'roo-select2-choices',
17542 cls: 'roo-select2-search-field',
17555 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17560 cls: 'form-hidden-field'
17566 if(!this.multiple && this.showToggleBtn){
17572 if (this.caret != false) {
17575 cls: 'fa fa-' + this.caret
17582 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17584 Roo.bootstrap.version == 3 ? caret : '',
17587 cls: 'combobox-clear',
17601 combobox.cls += ' roo-select2-container-multi';
17604 var required = this.allowBlank ? {
17606 style: 'display: none'
17609 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17610 tooltip : 'This field is required'
17613 var align = this.labelAlign || this.parentLabelAlign();
17615 if (align ==='left' && this.fieldLabel.length) {
17621 cls : 'control-label col-form-label',
17622 html : this.fieldLabel
17626 cls : 'roo-combobox-wrap ',
17633 var labelCfg = cfg.cn[1];
17634 var contentCfg = cfg.cn[2];
17637 if(this.indicatorpos == 'right'){
17642 cls : 'control-label col-form-label',
17646 html : this.fieldLabel
17652 cls : "roo-combobox-wrap ",
17660 labelCfg = cfg.cn[0];
17661 contentCfg = cfg.cn[1];
17666 if(this.labelWidth > 12){
17667 labelCfg.style = "width: " + this.labelWidth + 'px';
17670 if(this.labelWidth < 13 && this.labelmd == 0){
17671 this.labelmd = this.labelWidth;
17674 if(this.labellg > 0){
17675 labelCfg.cls += ' col-lg-' + this.labellg;
17676 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17679 if(this.labelmd > 0){
17680 labelCfg.cls += ' col-md-' + this.labelmd;
17681 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17684 if(this.labelsm > 0){
17685 labelCfg.cls += ' col-sm-' + this.labelsm;
17686 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17689 if(this.labelxs > 0){
17690 labelCfg.cls += ' col-xs-' + this.labelxs;
17691 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17695 } else if ( this.fieldLabel.length) {
17700 cls : 'control-label',
17701 html : this.fieldLabel
17712 if(this.indicatorpos == 'right'){
17716 cls : 'control-label',
17717 html : this.fieldLabel,
17735 var settings = this;
17737 ['xs','sm','md','lg'].map(function(size){
17738 if (settings[size]) {
17739 cfg.cls += ' col-' + size + '-' + settings[size];
17746 initTouchView : function()
17748 this.renderTouchView();
17750 this.touchViewEl.on('scroll', function(){
17751 this.el.dom.scrollTop = 0;
17754 this.originalValue = this.getValue();
17756 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17758 this.inputEl().on("click", this.showTouchView, this);
17759 if (this.triggerEl) {
17760 this.triggerEl.on("click", this.showTouchView, this);
17764 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17765 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17767 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17769 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17770 this.store.on('load', this.onTouchViewLoad, this);
17771 this.store.on('loadexception', this.onTouchViewLoadException, this);
17773 if(this.hiddenName){
17775 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17777 this.hiddenField.dom.value =
17778 this.hiddenValue !== undefined ? this.hiddenValue :
17779 this.value !== undefined ? this.value : '';
17781 this.el.dom.removeAttribute('name');
17782 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17786 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17787 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17790 if(this.removable && !this.multiple){
17791 var close = this.closeTriggerEl();
17793 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17794 close.on('click', this.removeBtnClick, this, close);
17798 * fix the bug in Safari iOS8
17800 this.inputEl().on("focus", function(e){
17801 document.activeElement.blur();
17804 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17811 renderTouchView : function()
17813 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17814 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17816 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17817 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17819 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17820 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17821 this.touchViewBodyEl.setStyle('overflow', 'auto');
17823 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17824 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17826 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17827 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17831 showTouchView : function()
17837 this.touchViewHeaderEl.hide();
17839 if(this.modalTitle.length){
17840 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17841 this.touchViewHeaderEl.show();
17844 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17845 this.touchViewEl.show();
17847 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17849 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17850 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17852 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17854 if(this.modalTitle.length){
17855 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17858 this.touchViewBodyEl.setHeight(bodyHeight);
17862 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17864 this.touchViewEl.addClass(['in','show']);
17867 if(this._touchViewMask){
17868 Roo.get(document.body).addClass("x-body-masked");
17869 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17870 this._touchViewMask.setStyle('z-index', 10000);
17871 this._touchViewMask.addClass('show');
17874 this.doTouchViewQuery();
17878 hideTouchView : function()
17880 this.touchViewEl.removeClass(['in','show']);
17884 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17886 this.touchViewEl.setStyle('display', 'none');
17889 if(this._touchViewMask){
17890 this._touchViewMask.removeClass('show');
17891 Roo.get(document.body).removeClass("x-body-masked");
17895 setTouchViewValue : function()
17902 Roo.each(this.tickItems, function(o){
17907 this.hideTouchView();
17910 doTouchViewQuery : function()
17919 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17923 if(!this.alwaysQuery || this.mode == 'local'){
17924 this.onTouchViewLoad();
17931 onTouchViewBeforeLoad : function(combo,opts)
17937 onTouchViewLoad : function()
17939 if(this.store.getCount() < 1){
17940 this.onTouchViewEmptyResults();
17944 this.clearTouchView();
17946 var rawValue = this.getRawValue();
17948 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17950 this.tickItems = [];
17952 this.store.data.each(function(d, rowIndex){
17953 var row = this.touchViewListGroup.createChild(template);
17955 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17956 row.addClass(d.data.cls);
17959 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17962 html : d.data[this.displayField]
17965 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17966 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17969 row.removeClass('selected');
17970 if(!this.multiple && this.valueField &&
17971 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17974 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17975 row.addClass('selected');
17978 if(this.multiple && this.valueField &&
17979 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17983 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17984 this.tickItems.push(d.data);
17987 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17991 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17993 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17995 if(this.modalTitle.length){
17996 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17999 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18001 if(this.mobile_restrict_height && listHeight < bodyHeight){
18002 this.touchViewBodyEl.setHeight(listHeight);
18007 if(firstChecked && listHeight > bodyHeight){
18008 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18013 onTouchViewLoadException : function()
18015 this.hideTouchView();
18018 onTouchViewEmptyResults : function()
18020 this.clearTouchView();
18022 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18024 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18028 clearTouchView : function()
18030 this.touchViewListGroup.dom.innerHTML = '';
18033 onTouchViewClick : function(e, el, o)
18035 e.preventDefault();
18038 var rowIndex = o.rowIndex;
18040 var r = this.store.getAt(rowIndex);
18042 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18044 if(!this.multiple){
18045 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18046 c.dom.removeAttribute('checked');
18049 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18051 this.setFromData(r.data);
18053 var close = this.closeTriggerEl();
18059 this.hideTouchView();
18061 this.fireEvent('select', this, r, rowIndex);
18066 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18067 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18068 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18072 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18073 this.addItem(r.data);
18074 this.tickItems.push(r.data);
18078 getAutoCreateNativeIOS : function()
18081 cls: 'form-group' //input-group,
18086 cls : 'roo-ios-select'
18090 combobox.name = this.name;
18093 if (this.disabled) {
18094 combobox.disabled = true;
18097 var settings = this;
18099 ['xs','sm','md','lg'].map(function(size){
18100 if (settings[size]) {
18101 cfg.cls += ' col-' + size + '-' + settings[size];
18111 initIOSView : function()
18113 this.store.on('load', this.onIOSViewLoad, this);
18118 onIOSViewLoad : function()
18120 if(this.store.getCount() < 1){
18124 this.clearIOSView();
18126 if(this.allowBlank) {
18128 var default_text = '-- SELECT --';
18130 if(this.placeholder.length){
18131 default_text = this.placeholder;
18134 if(this.emptyTitle.length){
18135 default_text += ' - ' + this.emptyTitle + ' -';
18138 var opt = this.inputEl().createChild({
18141 html : default_text
18145 o[this.valueField] = 0;
18146 o[this.displayField] = default_text;
18148 this.ios_options.push({
18155 this.store.data.each(function(d, rowIndex){
18159 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18160 html = d.data[this.displayField];
18165 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18166 value = d.data[this.valueField];
18175 if(this.value == d.data[this.valueField]){
18176 option['selected'] = true;
18179 var opt = this.inputEl().createChild(option);
18181 this.ios_options.push({
18188 this.inputEl().on('change', function(){
18189 this.fireEvent('select', this);
18194 clearIOSView: function()
18196 this.inputEl().dom.innerHTML = '';
18198 this.ios_options = [];
18201 setIOSValue: function(v)
18205 if(!this.ios_options){
18209 Roo.each(this.ios_options, function(opts){
18211 opts.el.dom.removeAttribute('selected');
18213 if(opts.data[this.valueField] != v){
18217 opts.el.dom.setAttribute('selected', true);
18223 * @cfg {Boolean} grow
18227 * @cfg {Number} growMin
18231 * @cfg {Number} growMax
18240 Roo.apply(Roo.bootstrap.ComboBox, {
18244 cls: 'modal-header',
18266 cls: 'list-group-item',
18270 cls: 'roo-combobox-list-group-item-value'
18274 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18288 listItemCheckbox : {
18290 cls: 'list-group-item',
18294 cls: 'roo-combobox-list-group-item-value'
18298 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18314 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18319 cls: 'modal-footer',
18327 cls: 'col-xs-6 text-left',
18330 cls: 'btn btn-danger roo-touch-view-cancel',
18336 cls: 'col-xs-6 text-right',
18339 cls: 'btn btn-success roo-touch-view-ok',
18350 Roo.apply(Roo.bootstrap.ComboBox, {
18352 touchViewTemplate : {
18354 cls: 'modal fade roo-combobox-touch-view',
18358 cls: 'modal-dialog',
18359 style : 'position:fixed', // we have to fix position....
18363 cls: 'modal-content',
18365 Roo.bootstrap.ComboBox.header,
18366 Roo.bootstrap.ComboBox.body,
18367 Roo.bootstrap.ComboBox.footer
18376 * Ext JS Library 1.1.1
18377 * Copyright(c) 2006-2007, Ext JS, LLC.
18379 * Originally Released Under LGPL - original licence link has changed is not relivant.
18382 * <script type="text/javascript">
18387 * @extends Roo.util.Observable
18388 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18389 * This class also supports single and multi selection modes. <br>
18390 * Create a data model bound view:
18392 var store = new Roo.data.Store(...);
18394 var view = new Roo.View({
18396 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18398 singleSelect: true,
18399 selectedClass: "ydataview-selected",
18403 // listen for node click?
18404 view.on("click", function(vw, index, node, e){
18405 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18409 dataModel.load("foobar.xml");
18411 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18413 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18414 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18416 * Note: old style constructor is still suported (container, template, config)
18419 * Create a new View
18420 * @param {Object} config The config object
18423 Roo.View = function(config, depreciated_tpl, depreciated_config){
18425 this.parent = false;
18427 if (typeof(depreciated_tpl) == 'undefined') {
18428 // new way.. - universal constructor.
18429 Roo.apply(this, config);
18430 this.el = Roo.get(this.el);
18433 this.el = Roo.get(config);
18434 this.tpl = depreciated_tpl;
18435 Roo.apply(this, depreciated_config);
18437 this.wrapEl = this.el.wrap().wrap();
18438 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18441 if(typeof(this.tpl) == "string"){
18442 this.tpl = new Roo.Template(this.tpl);
18444 // support xtype ctors..
18445 this.tpl = new Roo.factory(this.tpl, Roo);
18449 this.tpl.compile();
18454 * @event beforeclick
18455 * Fires before a click is processed. Returns false to cancel the default action.
18456 * @param {Roo.View} this
18457 * @param {Number} index The index of the target node
18458 * @param {HTMLElement} node The target node
18459 * @param {Roo.EventObject} e The raw event object
18461 "beforeclick" : true,
18464 * Fires when a template node is clicked.
18465 * @param {Roo.View} this
18466 * @param {Number} index The index of the target node
18467 * @param {HTMLElement} node The target node
18468 * @param {Roo.EventObject} e The raw event object
18473 * Fires when a template node is double clicked.
18474 * @param {Roo.View} this
18475 * @param {Number} index The index of the target node
18476 * @param {HTMLElement} node The target node
18477 * @param {Roo.EventObject} e The raw event object
18481 * @event contextmenu
18482 * Fires when a template node is right clicked.
18483 * @param {Roo.View} this
18484 * @param {Number} index The index of the target node
18485 * @param {HTMLElement} node The target node
18486 * @param {Roo.EventObject} e The raw event object
18488 "contextmenu" : true,
18490 * @event selectionchange
18491 * Fires when the selected nodes change.
18492 * @param {Roo.View} this
18493 * @param {Array} selections Array of the selected nodes
18495 "selectionchange" : true,
18498 * @event beforeselect
18499 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18500 * @param {Roo.View} this
18501 * @param {HTMLElement} node The node to be selected
18502 * @param {Array} selections Array of currently selected nodes
18504 "beforeselect" : true,
18506 * @event preparedata
18507 * Fires on every row to render, to allow you to change the data.
18508 * @param {Roo.View} this
18509 * @param {Object} data to be rendered (change this)
18511 "preparedata" : true
18519 "click": this.onClick,
18520 "dblclick": this.onDblClick,
18521 "contextmenu": this.onContextMenu,
18525 this.selections = [];
18527 this.cmp = new Roo.CompositeElementLite([]);
18529 this.store = Roo.factory(this.store, Roo.data);
18530 this.setStore(this.store, true);
18533 if ( this.footer && this.footer.xtype) {
18535 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18537 this.footer.dataSource = this.store;
18538 this.footer.container = fctr;
18539 this.footer = Roo.factory(this.footer, Roo);
18540 fctr.insertFirst(this.el);
18542 // this is a bit insane - as the paging toolbar seems to detach the el..
18543 // dom.parentNode.parentNode.parentNode
18544 // they get detached?
18548 Roo.View.superclass.constructor.call(this);
18553 Roo.extend(Roo.View, Roo.util.Observable, {
18556 * @cfg {Roo.data.Store} store Data store to load data from.
18561 * @cfg {String|Roo.Element} el The container element.
18566 * @cfg {String|Roo.Template} tpl The template used by this View
18570 * @cfg {String} dataName the named area of the template to use as the data area
18571 * Works with domtemplates roo-name="name"
18575 * @cfg {String} selectedClass The css class to add to selected nodes
18577 selectedClass : "x-view-selected",
18579 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18584 * @cfg {String} text to display on mask (default Loading)
18588 * @cfg {Boolean} multiSelect Allow multiple selection
18590 multiSelect : false,
18592 * @cfg {Boolean} singleSelect Allow single selection
18594 singleSelect: false,
18597 * @cfg {Boolean} toggleSelect - selecting
18599 toggleSelect : false,
18602 * @cfg {Boolean} tickable - selecting
18607 * Returns the element this view is bound to.
18608 * @return {Roo.Element}
18610 getEl : function(){
18611 return this.wrapEl;
18617 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18619 refresh : function(){
18620 //Roo.log('refresh');
18623 // if we are using something like 'domtemplate', then
18624 // the what gets used is:
18625 // t.applySubtemplate(NAME, data, wrapping data..)
18626 // the outer template then get' applied with
18627 // the store 'extra data'
18628 // and the body get's added to the
18629 // roo-name="data" node?
18630 // <span class='roo-tpl-{name}'></span> ?????
18634 this.clearSelections();
18635 this.el.update("");
18637 var records = this.store.getRange();
18638 if(records.length < 1) {
18640 // is this valid?? = should it render a template??
18642 this.el.update(this.emptyText);
18646 if (this.dataName) {
18647 this.el.update(t.apply(this.store.meta)); //????
18648 el = this.el.child('.roo-tpl-' + this.dataName);
18651 for(var i = 0, len = records.length; i < len; i++){
18652 var data = this.prepareData(records[i].data, i, records[i]);
18653 this.fireEvent("preparedata", this, data, i, records[i]);
18655 var d = Roo.apply({}, data);
18658 Roo.apply(d, {'roo-id' : Roo.id()});
18662 Roo.each(this.parent.item, function(item){
18663 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18666 Roo.apply(d, {'roo-data-checked' : 'checked'});
18670 html[html.length] = Roo.util.Format.trim(
18672 t.applySubtemplate(this.dataName, d, this.store.meta) :
18679 el.update(html.join(""));
18680 this.nodes = el.dom.childNodes;
18681 this.updateIndexes(0);
18686 * Function to override to reformat the data that is sent to
18687 * the template for each node.
18688 * DEPRICATED - use the preparedata event handler.
18689 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18690 * a JSON object for an UpdateManager bound view).
18692 prepareData : function(data, index, record)
18694 this.fireEvent("preparedata", this, data, index, record);
18698 onUpdate : function(ds, record){
18699 // Roo.log('on update');
18700 this.clearSelections();
18701 var index = this.store.indexOf(record);
18702 var n = this.nodes[index];
18703 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18704 n.parentNode.removeChild(n);
18705 this.updateIndexes(index, index);
18711 onAdd : function(ds, records, index)
18713 //Roo.log(['on Add', ds, records, index] );
18714 this.clearSelections();
18715 if(this.nodes.length == 0){
18719 var n = this.nodes[index];
18720 for(var i = 0, len = records.length; i < len; i++){
18721 var d = this.prepareData(records[i].data, i, records[i]);
18723 this.tpl.insertBefore(n, d);
18726 this.tpl.append(this.el, d);
18729 this.updateIndexes(index);
18732 onRemove : function(ds, record, index){
18733 // Roo.log('onRemove');
18734 this.clearSelections();
18735 var el = this.dataName ?
18736 this.el.child('.roo-tpl-' + this.dataName) :
18739 el.dom.removeChild(this.nodes[index]);
18740 this.updateIndexes(index);
18744 * Refresh an individual node.
18745 * @param {Number} index
18747 refreshNode : function(index){
18748 this.onUpdate(this.store, this.store.getAt(index));
18751 updateIndexes : function(startIndex, endIndex){
18752 var ns = this.nodes;
18753 startIndex = startIndex || 0;
18754 endIndex = endIndex || ns.length - 1;
18755 for(var i = startIndex; i <= endIndex; i++){
18756 ns[i].nodeIndex = i;
18761 * Changes the data store this view uses and refresh the view.
18762 * @param {Store} store
18764 setStore : function(store, initial){
18765 if(!initial && this.store){
18766 this.store.un("datachanged", this.refresh);
18767 this.store.un("add", this.onAdd);
18768 this.store.un("remove", this.onRemove);
18769 this.store.un("update", this.onUpdate);
18770 this.store.un("clear", this.refresh);
18771 this.store.un("beforeload", this.onBeforeLoad);
18772 this.store.un("load", this.onLoad);
18773 this.store.un("loadexception", this.onLoad);
18777 store.on("datachanged", this.refresh, this);
18778 store.on("add", this.onAdd, this);
18779 store.on("remove", this.onRemove, this);
18780 store.on("update", this.onUpdate, this);
18781 store.on("clear", this.refresh, this);
18782 store.on("beforeload", this.onBeforeLoad, this);
18783 store.on("load", this.onLoad, this);
18784 store.on("loadexception", this.onLoad, this);
18792 * onbeforeLoad - masks the loading area.
18795 onBeforeLoad : function(store,opts)
18797 //Roo.log('onBeforeLoad');
18799 this.el.update("");
18801 this.el.mask(this.mask ? this.mask : "Loading" );
18803 onLoad : function ()
18810 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18811 * @param {HTMLElement} node
18812 * @return {HTMLElement} The template node
18814 findItemFromChild : function(node){
18815 var el = this.dataName ?
18816 this.el.child('.roo-tpl-' + this.dataName,true) :
18819 if(!node || node.parentNode == el){
18822 var p = node.parentNode;
18823 while(p && p != el){
18824 if(p.parentNode == el){
18833 onClick : function(e){
18834 var item = this.findItemFromChild(e.getTarget());
18836 var index = this.indexOf(item);
18837 if(this.onItemClick(item, index, e) !== false){
18838 this.fireEvent("click", this, index, item, e);
18841 this.clearSelections();
18846 onContextMenu : function(e){
18847 var item = this.findItemFromChild(e.getTarget());
18849 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18854 onDblClick : function(e){
18855 var item = this.findItemFromChild(e.getTarget());
18857 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18861 onItemClick : function(item, index, e)
18863 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18866 if (this.toggleSelect) {
18867 var m = this.isSelected(item) ? 'unselect' : 'select';
18870 _t[m](item, true, false);
18873 if(this.multiSelect || this.singleSelect){
18874 if(this.multiSelect && e.shiftKey && this.lastSelection){
18875 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18877 this.select(item, this.multiSelect && e.ctrlKey);
18878 this.lastSelection = item;
18881 if(!this.tickable){
18882 e.preventDefault();
18890 * Get the number of selected nodes.
18893 getSelectionCount : function(){
18894 return this.selections.length;
18898 * Get the currently selected nodes.
18899 * @return {Array} An array of HTMLElements
18901 getSelectedNodes : function(){
18902 return this.selections;
18906 * Get the indexes of the selected nodes.
18909 getSelectedIndexes : function(){
18910 var indexes = [], s = this.selections;
18911 for(var i = 0, len = s.length; i < len; i++){
18912 indexes.push(s[i].nodeIndex);
18918 * Clear all selections
18919 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18921 clearSelections : function(suppressEvent){
18922 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18923 this.cmp.elements = this.selections;
18924 this.cmp.removeClass(this.selectedClass);
18925 this.selections = [];
18926 if(!suppressEvent){
18927 this.fireEvent("selectionchange", this, this.selections);
18933 * Returns true if the passed node is selected
18934 * @param {HTMLElement/Number} node The node or node index
18935 * @return {Boolean}
18937 isSelected : function(node){
18938 var s = this.selections;
18942 node = this.getNode(node);
18943 return s.indexOf(node) !== -1;
18948 * @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
18949 * @param {Boolean} keepExisting (optional) true to keep existing selections
18950 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18952 select : function(nodeInfo, keepExisting, suppressEvent){
18953 if(nodeInfo instanceof Array){
18955 this.clearSelections(true);
18957 for(var i = 0, len = nodeInfo.length; i < len; i++){
18958 this.select(nodeInfo[i], true, true);
18962 var node = this.getNode(nodeInfo);
18963 if(!node || this.isSelected(node)){
18964 return; // already selected.
18967 this.clearSelections(true);
18970 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18971 Roo.fly(node).addClass(this.selectedClass);
18972 this.selections.push(node);
18973 if(!suppressEvent){
18974 this.fireEvent("selectionchange", this, this.selections);
18982 * @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
18983 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18984 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18986 unselect : function(nodeInfo, keepExisting, suppressEvent)
18988 if(nodeInfo instanceof Array){
18989 Roo.each(this.selections, function(s) {
18990 this.unselect(s, nodeInfo);
18994 var node = this.getNode(nodeInfo);
18995 if(!node || !this.isSelected(node)){
18996 //Roo.log("not selected");
18997 return; // not selected.
19001 Roo.each(this.selections, function(s) {
19003 Roo.fly(node).removeClass(this.selectedClass);
19010 this.selections= ns;
19011 this.fireEvent("selectionchange", this, this.selections);
19015 * Gets a template node.
19016 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19017 * @return {HTMLElement} The node or null if it wasn't found
19019 getNode : function(nodeInfo){
19020 if(typeof nodeInfo == "string"){
19021 return document.getElementById(nodeInfo);
19022 }else if(typeof nodeInfo == "number"){
19023 return this.nodes[nodeInfo];
19029 * Gets a range template nodes.
19030 * @param {Number} startIndex
19031 * @param {Number} endIndex
19032 * @return {Array} An array of nodes
19034 getNodes : function(start, end){
19035 var ns = this.nodes;
19036 start = start || 0;
19037 end = typeof end == "undefined" ? ns.length - 1 : end;
19040 for(var i = start; i <= end; i++){
19044 for(var i = start; i >= end; i--){
19052 * Finds the index of the passed node
19053 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19054 * @return {Number} The index of the node or -1
19056 indexOf : function(node){
19057 node = this.getNode(node);
19058 if(typeof node.nodeIndex == "number"){
19059 return node.nodeIndex;
19061 var ns = this.nodes;
19062 for(var i = 0, len = ns.length; i < len; i++){
19073 * based on jquery fullcalendar
19077 Roo.bootstrap = Roo.bootstrap || {};
19079 * @class Roo.bootstrap.Calendar
19080 * @extends Roo.bootstrap.Component
19081 * Bootstrap Calendar class
19082 * @cfg {Boolean} loadMask (true|false) default false
19083 * @cfg {Object} header generate the user specific header of the calendar, default false
19086 * Create a new Container
19087 * @param {Object} config The config object
19092 Roo.bootstrap.Calendar = function(config){
19093 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19097 * Fires when a date is selected
19098 * @param {DatePicker} this
19099 * @param {Date} date The selected date
19103 * @event monthchange
19104 * Fires when the displayed month changes
19105 * @param {DatePicker} this
19106 * @param {Date} date The selected month
19108 'monthchange': true,
19110 * @event evententer
19111 * Fires when mouse over an event
19112 * @param {Calendar} this
19113 * @param {event} Event
19115 'evententer': true,
19117 * @event eventleave
19118 * Fires when the mouse leaves an
19119 * @param {Calendar} this
19122 'eventleave': true,
19124 * @event eventclick
19125 * Fires when the mouse click an
19126 * @param {Calendar} this
19135 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19138 * @cfg {Number} startDay
19139 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19147 getAutoCreate : function(){
19150 var fc_button = function(name, corner, style, content ) {
19151 return Roo.apply({},{
19153 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19155 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19158 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19169 style : 'width:100%',
19176 cls : 'fc-header-left',
19178 fc_button('prev', 'left', 'arrow', '‹' ),
19179 fc_button('next', 'right', 'arrow', '›' ),
19180 { tag: 'span', cls: 'fc-header-space' },
19181 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19189 cls : 'fc-header-center',
19193 cls: 'fc-header-title',
19196 html : 'month / year'
19204 cls : 'fc-header-right',
19206 /* fc_button('month', 'left', '', 'month' ),
19207 fc_button('week', '', '', 'week' ),
19208 fc_button('day', 'right', '', 'day' )
19220 header = this.header;
19223 var cal_heads = function() {
19225 // fixme - handle this.
19227 for (var i =0; i < Date.dayNames.length; i++) {
19228 var d = Date.dayNames[i];
19231 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19232 html : d.substring(0,3)
19236 ret[0].cls += ' fc-first';
19237 ret[6].cls += ' fc-last';
19240 var cal_cell = function(n) {
19243 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19248 cls: 'fc-day-number',
19252 cls: 'fc-day-content',
19256 style: 'position: relative;' // height: 17px;
19268 var cal_rows = function() {
19271 for (var r = 0; r < 6; r++) {
19278 for (var i =0; i < Date.dayNames.length; i++) {
19279 var d = Date.dayNames[i];
19280 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19283 row.cn[0].cls+=' fc-first';
19284 row.cn[0].cn[0].style = 'min-height:90px';
19285 row.cn[6].cls+=' fc-last';
19289 ret[0].cls += ' fc-first';
19290 ret[4].cls += ' fc-prev-last';
19291 ret[5].cls += ' fc-last';
19298 cls: 'fc-border-separate',
19299 style : 'width:100%',
19307 cls : 'fc-first fc-last',
19325 cls : 'fc-content',
19326 style : "position: relative;",
19329 cls : 'fc-view fc-view-month fc-grid',
19330 style : 'position: relative',
19331 unselectable : 'on',
19334 cls : 'fc-event-container',
19335 style : 'position:absolute;z-index:8;top:0;left:0;'
19353 initEvents : function()
19356 throw "can not find store for calendar";
19362 style: "text-align:center",
19366 style: "background-color:white;width:50%;margin:250 auto",
19370 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19381 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19383 var size = this.el.select('.fc-content', true).first().getSize();
19384 this.maskEl.setSize(size.width, size.height);
19385 this.maskEl.enableDisplayMode("block");
19386 if(!this.loadMask){
19387 this.maskEl.hide();
19390 this.store = Roo.factory(this.store, Roo.data);
19391 this.store.on('load', this.onLoad, this);
19392 this.store.on('beforeload', this.onBeforeLoad, this);
19396 this.cells = this.el.select('.fc-day',true);
19397 //Roo.log(this.cells);
19398 this.textNodes = this.el.query('.fc-day-number');
19399 this.cells.addClassOnOver('fc-state-hover');
19401 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19402 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19403 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19404 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19406 this.on('monthchange', this.onMonthChange, this);
19408 this.update(new Date().clearTime());
19411 resize : function() {
19412 var sz = this.el.getSize();
19414 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19415 this.el.select('.fc-day-content div',true).setHeight(34);
19420 showPrevMonth : function(e){
19421 this.update(this.activeDate.add("mo", -1));
19423 showToday : function(e){
19424 this.update(new Date().clearTime());
19427 showNextMonth : function(e){
19428 this.update(this.activeDate.add("mo", 1));
19432 showPrevYear : function(){
19433 this.update(this.activeDate.add("y", -1));
19437 showNextYear : function(){
19438 this.update(this.activeDate.add("y", 1));
19443 update : function(date)
19445 var vd = this.activeDate;
19446 this.activeDate = date;
19447 // if(vd && this.el){
19448 // var t = date.getTime();
19449 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19450 // Roo.log('using add remove');
19452 // this.fireEvent('monthchange', this, date);
19454 // this.cells.removeClass("fc-state-highlight");
19455 // this.cells.each(function(c){
19456 // if(c.dateValue == t){
19457 // c.addClass("fc-state-highlight");
19458 // setTimeout(function(){
19459 // try{c.dom.firstChild.focus();}catch(e){}
19469 var days = date.getDaysInMonth();
19471 var firstOfMonth = date.getFirstDateOfMonth();
19472 var startingPos = firstOfMonth.getDay()-this.startDay;
19474 if(startingPos < this.startDay){
19478 var pm = date.add(Date.MONTH, -1);
19479 var prevStart = pm.getDaysInMonth()-startingPos;
19481 this.cells = this.el.select('.fc-day',true);
19482 this.textNodes = this.el.query('.fc-day-number');
19483 this.cells.addClassOnOver('fc-state-hover');
19485 var cells = this.cells.elements;
19486 var textEls = this.textNodes;
19488 Roo.each(cells, function(cell){
19489 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19492 days += startingPos;
19494 // convert everything to numbers so it's fast
19495 var day = 86400000;
19496 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19499 //Roo.log(prevStart);
19501 var today = new Date().clearTime().getTime();
19502 var sel = date.clearTime().getTime();
19503 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19504 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19505 var ddMatch = this.disabledDatesRE;
19506 var ddText = this.disabledDatesText;
19507 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19508 var ddaysText = this.disabledDaysText;
19509 var format = this.format;
19511 var setCellClass = function(cal, cell){
19515 //Roo.log('set Cell Class');
19517 var t = d.getTime();
19521 cell.dateValue = t;
19523 cell.className += " fc-today";
19524 cell.className += " fc-state-highlight";
19525 cell.title = cal.todayText;
19528 // disable highlight in other month..
19529 //cell.className += " fc-state-highlight";
19534 cell.className = " fc-state-disabled";
19535 cell.title = cal.minText;
19539 cell.className = " fc-state-disabled";
19540 cell.title = cal.maxText;
19544 if(ddays.indexOf(d.getDay()) != -1){
19545 cell.title = ddaysText;
19546 cell.className = " fc-state-disabled";
19549 if(ddMatch && format){
19550 var fvalue = d.dateFormat(format);
19551 if(ddMatch.test(fvalue)){
19552 cell.title = ddText.replace("%0", fvalue);
19553 cell.className = " fc-state-disabled";
19557 if (!cell.initialClassName) {
19558 cell.initialClassName = cell.dom.className;
19561 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19566 for(; i < startingPos; i++) {
19567 textEls[i].innerHTML = (++prevStart);
19568 d.setDate(d.getDate()+1);
19570 cells[i].className = "fc-past fc-other-month";
19571 setCellClass(this, cells[i]);
19576 for(; i < days; i++){
19577 intDay = i - startingPos + 1;
19578 textEls[i].innerHTML = (intDay);
19579 d.setDate(d.getDate()+1);
19581 cells[i].className = ''; // "x-date-active";
19582 setCellClass(this, cells[i]);
19586 for(; i < 42; i++) {
19587 textEls[i].innerHTML = (++extraDays);
19588 d.setDate(d.getDate()+1);
19590 cells[i].className = "fc-future fc-other-month";
19591 setCellClass(this, cells[i]);
19594 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19596 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19598 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19599 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19601 if(totalRows != 6){
19602 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19603 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19606 this.fireEvent('monthchange', this, date);
19610 if(!this.internalRender){
19611 var main = this.el.dom.firstChild;
19612 var w = main.offsetWidth;
19613 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19614 Roo.fly(main).setWidth(w);
19615 this.internalRender = true;
19616 // opera does not respect the auto grow header center column
19617 // then, after it gets a width opera refuses to recalculate
19618 // without a second pass
19619 if(Roo.isOpera && !this.secondPass){
19620 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19621 this.secondPass = true;
19622 this.update.defer(10, this, [date]);
19629 findCell : function(dt) {
19630 dt = dt.clearTime().getTime();
19632 this.cells.each(function(c){
19633 //Roo.log("check " +c.dateValue + '?=' + dt);
19634 if(c.dateValue == dt){
19644 findCells : function(ev) {
19645 var s = ev.start.clone().clearTime().getTime();
19647 var e= ev.end.clone().clearTime().getTime();
19650 this.cells.each(function(c){
19651 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19653 if(c.dateValue > e){
19656 if(c.dateValue < s){
19665 // findBestRow: function(cells)
19669 // for (var i =0 ; i < cells.length;i++) {
19670 // ret = Math.max(cells[i].rows || 0,ret);
19677 addItem : function(ev)
19679 // look for vertical location slot in
19680 var cells = this.findCells(ev);
19682 // ev.row = this.findBestRow(cells);
19684 // work out the location.
19688 for(var i =0; i < cells.length; i++) {
19690 cells[i].row = cells[0].row;
19693 cells[i].row = cells[i].row + 1;
19703 if (crow.start.getY() == cells[i].getY()) {
19705 crow.end = cells[i];
19722 cells[0].events.push(ev);
19724 this.calevents.push(ev);
19727 clearEvents: function() {
19729 if(!this.calevents){
19733 Roo.each(this.cells.elements, function(c){
19739 Roo.each(this.calevents, function(e) {
19740 Roo.each(e.els, function(el) {
19741 el.un('mouseenter' ,this.onEventEnter, this);
19742 el.un('mouseleave' ,this.onEventLeave, this);
19747 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19753 renderEvents: function()
19757 this.cells.each(function(c) {
19766 if(c.row != c.events.length){
19767 r = 4 - (4 - (c.row - c.events.length));
19770 c.events = ev.slice(0, r);
19771 c.more = ev.slice(r);
19773 if(c.more.length && c.more.length == 1){
19774 c.events.push(c.more.pop());
19777 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19781 this.cells.each(function(c) {
19783 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19786 for (var e = 0; e < c.events.length; e++){
19787 var ev = c.events[e];
19788 var rows = ev.rows;
19790 for(var i = 0; i < rows.length; i++) {
19792 // how many rows should it span..
19795 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19796 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19798 unselectable : "on",
19801 cls: 'fc-event-inner',
19805 // cls: 'fc-event-time',
19806 // html : cells.length > 1 ? '' : ev.time
19810 cls: 'fc-event-title',
19811 html : String.format('{0}', ev.title)
19818 cls: 'ui-resizable-handle ui-resizable-e',
19819 html : '  '
19826 cfg.cls += ' fc-event-start';
19828 if ((i+1) == rows.length) {
19829 cfg.cls += ' fc-event-end';
19832 var ctr = _this.el.select('.fc-event-container',true).first();
19833 var cg = ctr.createChild(cfg);
19835 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19836 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19838 var r = (c.more.length) ? 1 : 0;
19839 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19840 cg.setWidth(ebox.right - sbox.x -2);
19842 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19843 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19844 cg.on('click', _this.onEventClick, _this, ev);
19855 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19856 style : 'position: absolute',
19857 unselectable : "on",
19860 cls: 'fc-event-inner',
19864 cls: 'fc-event-title',
19872 cls: 'ui-resizable-handle ui-resizable-e',
19873 html : '  '
19879 var ctr = _this.el.select('.fc-event-container',true).first();
19880 var cg = ctr.createChild(cfg);
19882 var sbox = c.select('.fc-day-content',true).first().getBox();
19883 var ebox = c.select('.fc-day-content',true).first().getBox();
19885 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19886 cg.setWidth(ebox.right - sbox.x -2);
19888 cg.on('click', _this.onMoreEventClick, _this, c.more);
19898 onEventEnter: function (e, el,event,d) {
19899 this.fireEvent('evententer', this, el, event);
19902 onEventLeave: function (e, el,event,d) {
19903 this.fireEvent('eventleave', this, el, event);
19906 onEventClick: function (e, el,event,d) {
19907 this.fireEvent('eventclick', this, el, event);
19910 onMonthChange: function () {
19914 onMoreEventClick: function(e, el, more)
19918 this.calpopover.placement = 'right';
19919 this.calpopover.setTitle('More');
19921 this.calpopover.setContent('');
19923 var ctr = this.calpopover.el.select('.popover-content', true).first();
19925 Roo.each(more, function(m){
19927 cls : 'fc-event-hori fc-event-draggable',
19930 var cg = ctr.createChild(cfg);
19932 cg.on('click', _this.onEventClick, _this, m);
19935 this.calpopover.show(el);
19940 onLoad: function ()
19942 this.calevents = [];
19945 if(this.store.getCount() > 0){
19946 this.store.data.each(function(d){
19949 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19950 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19951 time : d.data.start_time,
19952 title : d.data.title,
19953 description : d.data.description,
19954 venue : d.data.venue
19959 this.renderEvents();
19961 if(this.calevents.length && this.loadMask){
19962 this.maskEl.hide();
19966 onBeforeLoad: function()
19968 this.clearEvents();
19970 this.maskEl.show();
19984 * @class Roo.bootstrap.Popover
19985 * @extends Roo.bootstrap.Component
19986 * Bootstrap Popover class
19987 * @cfg {String} html contents of the popover (or false to use children..)
19988 * @cfg {String} title of popover (or false to hide)
19989 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19990 * @cfg {String} trigger click || hover (or false to trigger manually)
19991 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19992 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19993 * - if false and it has a 'parent' then it will be automatically added to that element
19994 * - if string - Roo.get will be called
19995 * @cfg {Number} delay - delay before showing
19998 * Create a new Popover
19999 * @param {Object} config The config object
20002 Roo.bootstrap.Popover = function(config){
20003 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20009 * After the popover show
20011 * @param {Roo.bootstrap.Popover} this
20016 * After the popover hide
20018 * @param {Roo.bootstrap.Popover} this
20024 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20029 placement : 'right',
20030 trigger : 'hover', // hover
20036 can_build_overlaid : false,
20038 maskEl : false, // the mask element
20041 alignEl : false, // when show is called with an element - this get's stored.
20043 getChildContainer : function()
20045 return this.contentEl;
20048 getPopoverHeader : function()
20050 this.title = true; // flag not to hide it..
20051 this.headerEl.addClass('p-0');
20052 return this.headerEl
20056 getAutoCreate : function(){
20059 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20060 style: 'display:block',
20066 cls : 'popover-inner ',
20070 cls: 'popover-title popover-header',
20071 html : this.title === false ? '' : this.title
20074 cls : 'popover-content popover-body ' + (this.cls || ''),
20075 html : this.html || ''
20086 * @param {string} the title
20088 setTitle: function(str)
20092 this.headerEl.dom.innerHTML = str;
20097 * @param {string} the body content
20099 setContent: function(str)
20102 if (this.contentEl) {
20103 this.contentEl.dom.innerHTML = str;
20107 // as it get's added to the bottom of the page.
20108 onRender : function(ct, position)
20110 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20115 var cfg = Roo.apply({}, this.getAutoCreate());
20119 cfg.cls += ' ' + this.cls;
20122 cfg.style = this.style;
20124 //Roo.log("adding to ");
20125 this.el = Roo.get(document.body).createChild(cfg, position);
20126 // Roo.log(this.el);
20129 this.contentEl = this.el.select('.popover-content',true).first();
20130 this.headerEl = this.el.select('.popover-title',true).first();
20133 if(typeof(this.items) != 'undefined'){
20134 var items = this.items;
20137 for(var i =0;i < items.length;i++) {
20138 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20142 this.items = nitems;
20144 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20145 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20152 resizeMask : function()
20154 this.maskEl.setSize(
20155 Roo.lib.Dom.getViewWidth(true),
20156 Roo.lib.Dom.getViewHeight(true)
20160 initEvents : function()
20164 Roo.bootstrap.Popover.register(this);
20167 this.arrowEl = this.el.select('.arrow',true).first();
20168 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20169 this.el.enableDisplayMode('block');
20173 if (this.over === false && !this.parent()) {
20176 if (this.triggers === false) {
20181 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20182 var triggers = this.trigger ? this.trigger.split(' ') : [];
20183 Roo.each(triggers, function(trigger) {
20185 if (trigger == 'click') {
20186 on_el.on('click', this.toggle, this);
20187 } else if (trigger != 'manual') {
20188 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20189 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20191 on_el.on(eventIn ,this.enter, this);
20192 on_el.on(eventOut, this.leave, this);
20202 toggle : function () {
20203 this.hoverState == 'in' ? this.leave() : this.enter();
20206 enter : function () {
20208 clearTimeout(this.timeout);
20210 this.hoverState = 'in';
20212 if (!this.delay || !this.delay.show) {
20217 this.timeout = setTimeout(function () {
20218 if (_t.hoverState == 'in') {
20221 }, this.delay.show)
20224 leave : function() {
20225 clearTimeout(this.timeout);
20227 this.hoverState = 'out';
20229 if (!this.delay || !this.delay.hide) {
20234 this.timeout = setTimeout(function () {
20235 if (_t.hoverState == 'out') {
20238 }, this.delay.hide)
20242 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20243 * @param {string} (left|right|top|bottom) position
20245 show : function (on_el, placement)
20247 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20248 on_el = on_el || false; // default to false
20251 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20252 on_el = this.parent().el;
20253 } else if (this.over) {
20254 on_el = Roo.get(this.over);
20259 this.alignEl = Roo.get( on_el );
20262 this.render(document.body);
20268 if (this.title === false) {
20269 this.headerEl.hide();
20274 this.el.dom.style.display = 'block';
20277 if (this.alignEl) {
20278 this.updatePosition(this.placement, true);
20281 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20282 var es = this.el.getSize();
20283 var x = Roo.lib.Dom.getViewWidth()/2;
20284 var y = Roo.lib.Dom.getViewHeight()/2;
20285 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20290 //var arrow = this.el.select('.arrow',true).first();
20291 //arrow.set(align[2],
20293 this.el.addClass('in');
20297 this.hoverState = 'in';
20300 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20301 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20302 this.maskEl.dom.style.display = 'block';
20303 this.maskEl.addClass('show');
20305 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20307 this.fireEvent('show', this);
20311 * fire this manually after loading a grid in the table for example
20312 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20313 * @param {Boolean} try and move it if we cant get right position.
20315 updatePosition : function(placement, try_move)
20317 // allow for calling with no parameters
20318 placement = placement ? placement : this.placement;
20319 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20321 this.el.removeClass([
20322 'fade','top','bottom', 'left', 'right','in',
20323 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20325 this.el.addClass(placement + ' bs-popover-' + placement);
20327 if (!this.alignEl ) {
20331 switch (placement) {
20333 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20334 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20335 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20336 //normal display... or moved up/down.
20337 this.el.setXY(offset);
20338 var xy = this.alignEl.getAnchorXY('tr', false);
20340 this.arrowEl.setXY(xy);
20343 // continue through...
20344 return this.updatePosition('left', false);
20348 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20349 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20350 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20351 //normal display... or moved up/down.
20352 this.el.setXY(offset);
20353 var xy = this.alignEl.getAnchorXY('tl', false);
20354 xy[0]-=10;xy[1]+=5; // << fix me
20355 this.arrowEl.setXY(xy);
20359 return this.updatePosition('right', false);
20362 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20363 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20364 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20365 //normal display... or moved up/down.
20366 this.el.setXY(offset);
20367 var xy = this.alignEl.getAnchorXY('t', false);
20368 xy[1]-=10; // << fix me
20369 this.arrowEl.setXY(xy);
20373 return this.updatePosition('bottom', false);
20376 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20377 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20378 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20379 //normal display... or moved up/down.
20380 this.el.setXY(offset);
20381 var xy = this.alignEl.getAnchorXY('b', false);
20382 xy[1]+=2; // << fix me
20383 this.arrowEl.setXY(xy);
20387 return this.updatePosition('top', false);
20398 this.el.setXY([0,0]);
20399 this.el.removeClass('in');
20401 this.hoverState = null;
20402 this.maskEl.hide(); // always..
20403 this.fireEvent('hide', this);
20409 Roo.apply(Roo.bootstrap.Popover, {
20412 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20413 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20414 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20415 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20420 clickHander : false,
20424 onMouseDown : function(e)
20426 if (this.popups.length && !e.getTarget(".roo-popover")) {
20427 /// what is nothing is showing..
20436 register : function(popup)
20438 if (!Roo.bootstrap.Popover.clickHandler) {
20439 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20441 // hide other popups.
20442 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
20443 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
20444 this.hideAll(); //<< why?
20445 //this.popups.push(popup);
20447 hideAll : function()
20449 this.popups.forEach(function(p) {
20453 onShow : function() {
20454 Roo.bootstrap.Popover.popups.push(this);
20456 onHide : function() {
20457 Roo.bootstrap.Popover.popups.remove(this);
20463 * Card header - holder for the card header elements.
20468 * @class Roo.bootstrap.PopoverNav
20469 * @extends Roo.bootstrap.NavGroup
20470 * Bootstrap Popover header navigation class
20472 * Create a new Popover Header Navigation
20473 * @param {Object} config The config object
20476 Roo.bootstrap.PopoverNav = function(config){
20477 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20480 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20483 container_method : 'getPopoverHeader'
20501 * @class Roo.bootstrap.Progress
20502 * @extends Roo.bootstrap.Component
20503 * Bootstrap Progress class
20504 * @cfg {Boolean} striped striped of the progress bar
20505 * @cfg {Boolean} active animated of the progress bar
20509 * Create a new Progress
20510 * @param {Object} config The config object
20513 Roo.bootstrap.Progress = function(config){
20514 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20517 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20522 getAutoCreate : function(){
20530 cfg.cls += ' progress-striped';
20534 cfg.cls += ' active';
20553 * @class Roo.bootstrap.ProgressBar
20554 * @extends Roo.bootstrap.Component
20555 * Bootstrap ProgressBar class
20556 * @cfg {Number} aria_valuenow aria-value now
20557 * @cfg {Number} aria_valuemin aria-value min
20558 * @cfg {Number} aria_valuemax aria-value max
20559 * @cfg {String} label label for the progress bar
20560 * @cfg {String} panel (success | info | warning | danger )
20561 * @cfg {String} role role of the progress bar
20562 * @cfg {String} sr_only text
20566 * Create a new ProgressBar
20567 * @param {Object} config The config object
20570 Roo.bootstrap.ProgressBar = function(config){
20571 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20574 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20578 aria_valuemax : 100,
20584 getAutoCreate : function()
20589 cls: 'progress-bar',
20590 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20602 cfg.role = this.role;
20605 if(this.aria_valuenow){
20606 cfg['aria-valuenow'] = this.aria_valuenow;
20609 if(this.aria_valuemin){
20610 cfg['aria-valuemin'] = this.aria_valuemin;
20613 if(this.aria_valuemax){
20614 cfg['aria-valuemax'] = this.aria_valuemax;
20617 if(this.label && !this.sr_only){
20618 cfg.html = this.label;
20622 cfg.cls += ' progress-bar-' + this.panel;
20628 update : function(aria_valuenow)
20630 this.aria_valuenow = aria_valuenow;
20632 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20647 * @class Roo.bootstrap.TabGroup
20648 * @extends Roo.bootstrap.Column
20649 * Bootstrap Column class
20650 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20651 * @cfg {Boolean} carousel true to make the group behave like a carousel
20652 * @cfg {Boolean} bullets show bullets for the panels
20653 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20654 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20655 * @cfg {Boolean} showarrow (true|false) show arrow default true
20658 * Create a new TabGroup
20659 * @param {Object} config The config object
20662 Roo.bootstrap.TabGroup = function(config){
20663 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20665 this.navId = Roo.id();
20668 Roo.bootstrap.TabGroup.register(this);
20672 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20675 transition : false,
20680 slideOnTouch : false,
20683 getAutoCreate : function()
20685 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20687 cfg.cls += ' tab-content';
20689 if (this.carousel) {
20690 cfg.cls += ' carousel slide';
20693 cls : 'carousel-inner',
20697 if(this.bullets && !Roo.isTouch){
20700 cls : 'carousel-bullets',
20704 if(this.bullets_cls){
20705 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20712 cfg.cn[0].cn.push(bullets);
20715 if(this.showarrow){
20716 cfg.cn[0].cn.push({
20718 class : 'carousel-arrow',
20722 class : 'carousel-prev',
20726 class : 'fa fa-chevron-left'
20732 class : 'carousel-next',
20736 class : 'fa fa-chevron-right'
20749 initEvents: function()
20751 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20752 // this.el.on("touchstart", this.onTouchStart, this);
20755 if(this.autoslide){
20758 this.slideFn = window.setInterval(function() {
20759 _this.showPanelNext();
20763 if(this.showarrow){
20764 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20765 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20771 // onTouchStart : function(e, el, o)
20773 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20777 // this.showPanelNext();
20781 getChildContainer : function()
20783 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20787 * register a Navigation item
20788 * @param {Roo.bootstrap.NavItem} the navitem to add
20790 register : function(item)
20792 this.tabs.push( item);
20793 item.navId = this.navId; // not really needed..
20798 getActivePanel : function()
20801 Roo.each(this.tabs, function(t) {
20811 getPanelByName : function(n)
20814 Roo.each(this.tabs, function(t) {
20815 if (t.tabId == n) {
20823 indexOfPanel : function(p)
20826 Roo.each(this.tabs, function(t,i) {
20827 if (t.tabId == p.tabId) {
20836 * show a specific panel
20837 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20838 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20840 showPanel : function (pan)
20842 if(this.transition || typeof(pan) == 'undefined'){
20843 Roo.log("waiting for the transitionend");
20847 if (typeof(pan) == 'number') {
20848 pan = this.tabs[pan];
20851 if (typeof(pan) == 'string') {
20852 pan = this.getPanelByName(pan);
20855 var cur = this.getActivePanel();
20858 Roo.log('pan or acitve pan is undefined');
20862 if (pan.tabId == this.getActivePanel().tabId) {
20866 if (false === cur.fireEvent('beforedeactivate')) {
20870 if(this.bullets > 0 && !Roo.isTouch){
20871 this.setActiveBullet(this.indexOfPanel(pan));
20874 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20876 //class="carousel-item carousel-item-next carousel-item-left"
20878 this.transition = true;
20879 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20880 var lr = dir == 'next' ? 'left' : 'right';
20881 pan.el.addClass(dir); // or prev
20882 pan.el.addClass('carousel-item-' + dir); // or prev
20883 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20884 cur.el.addClass(lr); // or right
20885 pan.el.addClass(lr);
20886 cur.el.addClass('carousel-item-' +lr); // or right
20887 pan.el.addClass('carousel-item-' +lr);
20891 cur.el.on('transitionend', function() {
20892 Roo.log("trans end?");
20894 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20895 pan.setActive(true);
20897 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20898 cur.setActive(false);
20900 _this.transition = false;
20902 }, this, { single: true } );
20907 cur.setActive(false);
20908 pan.setActive(true);
20913 showPanelNext : function()
20915 var i = this.indexOfPanel(this.getActivePanel());
20917 if (i >= this.tabs.length - 1 && !this.autoslide) {
20921 if (i >= this.tabs.length - 1 && this.autoslide) {
20925 this.showPanel(this.tabs[i+1]);
20928 showPanelPrev : function()
20930 var i = this.indexOfPanel(this.getActivePanel());
20932 if (i < 1 && !this.autoslide) {
20936 if (i < 1 && this.autoslide) {
20937 i = this.tabs.length;
20940 this.showPanel(this.tabs[i-1]);
20944 addBullet: function()
20946 if(!this.bullets || Roo.isTouch){
20949 var ctr = this.el.select('.carousel-bullets',true).first();
20950 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20951 var bullet = ctr.createChild({
20952 cls : 'bullet bullet-' + i
20953 },ctr.dom.lastChild);
20958 bullet.on('click', (function(e, el, o, ii, t){
20960 e.preventDefault();
20962 this.showPanel(ii);
20964 if(this.autoslide && this.slideFn){
20965 clearInterval(this.slideFn);
20966 this.slideFn = window.setInterval(function() {
20967 _this.showPanelNext();
20971 }).createDelegate(this, [i, bullet], true));
20976 setActiveBullet : function(i)
20982 Roo.each(this.el.select('.bullet', true).elements, function(el){
20983 el.removeClass('selected');
20986 var bullet = this.el.select('.bullet-' + i, true).first();
20992 bullet.addClass('selected');
21003 Roo.apply(Roo.bootstrap.TabGroup, {
21007 * register a Navigation Group
21008 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21010 register : function(navgrp)
21012 this.groups[navgrp.navId] = navgrp;
21016 * fetch a Navigation Group based on the navigation ID
21017 * if one does not exist , it will get created.
21018 * @param {string} the navgroup to add
21019 * @returns {Roo.bootstrap.NavGroup} the navgroup
21021 get: function(navId) {
21022 if (typeof(this.groups[navId]) == 'undefined') {
21023 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21025 return this.groups[navId] ;
21040 * @class Roo.bootstrap.TabPanel
21041 * @extends Roo.bootstrap.Component
21042 * Bootstrap TabPanel class
21043 * @cfg {Boolean} active panel active
21044 * @cfg {String} html panel content
21045 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21046 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21047 * @cfg {String} href click to link..
21048 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21052 * Create a new TabPanel
21053 * @param {Object} config The config object
21056 Roo.bootstrap.TabPanel = function(config){
21057 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21061 * Fires when the active status changes
21062 * @param {Roo.bootstrap.TabPanel} this
21063 * @param {Boolean} state the new state
21068 * @event beforedeactivate
21069 * Fires before a tab is de-activated - can be used to do validation on a form.
21070 * @param {Roo.bootstrap.TabPanel} this
21071 * @return {Boolean} false if there is an error
21074 'beforedeactivate': true
21077 this.tabId = this.tabId || Roo.id();
21081 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21088 touchSlide : false,
21089 getAutoCreate : function(){
21094 // item is needed for carousel - not sure if it has any effect otherwise
21095 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21096 html: this.html || ''
21100 cfg.cls += ' active';
21104 cfg.tabId = this.tabId;
21112 initEvents: function()
21114 var p = this.parent();
21116 this.navId = this.navId || p.navId;
21118 if (typeof(this.navId) != 'undefined') {
21119 // not really needed.. but just in case.. parent should be a NavGroup.
21120 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21124 var i = tg.tabs.length - 1;
21126 if(this.active && tg.bullets > 0 && i < tg.bullets){
21127 tg.setActiveBullet(i);
21131 this.el.on('click', this.onClick, this);
21133 if(Roo.isTouch && this.touchSlide){
21134 this.el.on("touchstart", this.onTouchStart, this);
21135 this.el.on("touchmove", this.onTouchMove, this);
21136 this.el.on("touchend", this.onTouchEnd, this);
21141 onRender : function(ct, position)
21143 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21146 setActive : function(state)
21148 Roo.log("panel - set active " + this.tabId + "=" + state);
21150 this.active = state;
21152 this.el.removeClass('active');
21154 } else if (!this.el.hasClass('active')) {
21155 this.el.addClass('active');
21158 this.fireEvent('changed', this, state);
21161 onClick : function(e)
21163 e.preventDefault();
21165 if(!this.href.length){
21169 window.location.href = this.href;
21178 onTouchStart : function(e)
21180 this.swiping = false;
21182 this.startX = e.browserEvent.touches[0].clientX;
21183 this.startY = e.browserEvent.touches[0].clientY;
21186 onTouchMove : function(e)
21188 this.swiping = true;
21190 this.endX = e.browserEvent.touches[0].clientX;
21191 this.endY = e.browserEvent.touches[0].clientY;
21194 onTouchEnd : function(e)
21201 var tabGroup = this.parent();
21203 if(this.endX > this.startX){ // swiping right
21204 tabGroup.showPanelPrev();
21208 if(this.startX > this.endX){ // swiping left
21209 tabGroup.showPanelNext();
21228 * @class Roo.bootstrap.DateField
21229 * @extends Roo.bootstrap.Input
21230 * Bootstrap DateField class
21231 * @cfg {Number} weekStart default 0
21232 * @cfg {String} viewMode default empty, (months|years)
21233 * @cfg {String} minViewMode default empty, (months|years)
21234 * @cfg {Number} startDate default -Infinity
21235 * @cfg {Number} endDate default Infinity
21236 * @cfg {Boolean} todayHighlight default false
21237 * @cfg {Boolean} todayBtn default false
21238 * @cfg {Boolean} calendarWeeks default false
21239 * @cfg {Object} daysOfWeekDisabled default empty
21240 * @cfg {Boolean} singleMode default false (true | false)
21242 * @cfg {Boolean} keyboardNavigation default true
21243 * @cfg {String} language default en
21246 * Create a new DateField
21247 * @param {Object} config The config object
21250 Roo.bootstrap.DateField = function(config){
21251 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21255 * Fires when this field show.
21256 * @param {Roo.bootstrap.DateField} this
21257 * @param {Mixed} date The date value
21262 * Fires when this field hide.
21263 * @param {Roo.bootstrap.DateField} this
21264 * @param {Mixed} date The date value
21269 * Fires when select a date.
21270 * @param {Roo.bootstrap.DateField} this
21271 * @param {Mixed} date The date value
21275 * @event beforeselect
21276 * Fires when before select a date.
21277 * @param {Roo.bootstrap.DateField} this
21278 * @param {Mixed} date The date value
21280 beforeselect : true
21284 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21287 * @cfg {String} format
21288 * The default date format string which can be overriden for localization support. The format must be
21289 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21293 * @cfg {String} altFormats
21294 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21295 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21297 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21305 todayHighlight : false,
21311 keyboardNavigation: true,
21313 calendarWeeks: false,
21315 startDate: -Infinity,
21319 daysOfWeekDisabled: [],
21323 singleMode : false,
21325 UTCDate: function()
21327 return new Date(Date.UTC.apply(Date, arguments));
21330 UTCToday: function()
21332 var today = new Date();
21333 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21336 getDate: function() {
21337 var d = this.getUTCDate();
21338 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21341 getUTCDate: function() {
21345 setDate: function(d) {
21346 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21349 setUTCDate: function(d) {
21351 this.setValue(this.formatDate(this.date));
21354 onRender: function(ct, position)
21357 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21359 this.language = this.language || 'en';
21360 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21361 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21363 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21364 this.format = this.format || 'm/d/y';
21365 this.isInline = false;
21366 this.isInput = true;
21367 this.component = this.el.select('.add-on', true).first() || false;
21368 this.component = (this.component && this.component.length === 0) ? false : this.component;
21369 this.hasInput = this.component && this.inputEl().length;
21371 if (typeof(this.minViewMode === 'string')) {
21372 switch (this.minViewMode) {
21374 this.minViewMode = 1;
21377 this.minViewMode = 2;
21380 this.minViewMode = 0;
21385 if (typeof(this.viewMode === 'string')) {
21386 switch (this.viewMode) {
21399 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21401 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21403 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21405 this.picker().on('mousedown', this.onMousedown, this);
21406 this.picker().on('click', this.onClick, this);
21408 this.picker().addClass('datepicker-dropdown');
21410 this.startViewMode = this.viewMode;
21412 if(this.singleMode){
21413 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21414 v.setVisibilityMode(Roo.Element.DISPLAY);
21418 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21419 v.setStyle('width', '189px');
21423 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21424 if(!this.calendarWeeks){
21429 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21430 v.attr('colspan', function(i, val){
21431 return parseInt(val) + 1;
21436 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21438 this.setStartDate(this.startDate);
21439 this.setEndDate(this.endDate);
21441 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21448 if(this.isInline) {
21453 picker : function()
21455 return this.pickerEl;
21456 // return this.el.select('.datepicker', true).first();
21459 fillDow: function()
21461 var dowCnt = this.weekStart;
21470 if(this.calendarWeeks){
21478 while (dowCnt < this.weekStart + 7) {
21482 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21486 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21489 fillMonths: function()
21492 var months = this.picker().select('>.datepicker-months td', true).first();
21494 months.dom.innerHTML = '';
21500 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21503 months.createChild(month);
21510 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;
21512 if (this.date < this.startDate) {
21513 this.viewDate = new Date(this.startDate);
21514 } else if (this.date > this.endDate) {
21515 this.viewDate = new Date(this.endDate);
21517 this.viewDate = new Date(this.date);
21525 var d = new Date(this.viewDate),
21526 year = d.getUTCFullYear(),
21527 month = d.getUTCMonth(),
21528 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21529 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21530 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21531 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21532 currentDate = this.date && this.date.valueOf(),
21533 today = this.UTCToday();
21535 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21537 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21539 // this.picker.select('>tfoot th.today').
21540 // .text(dates[this.language].today)
21541 // .toggle(this.todayBtn !== false);
21543 this.updateNavArrows();
21546 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21548 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21550 prevMonth.setUTCDate(day);
21552 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21554 var nextMonth = new Date(prevMonth);
21556 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21558 nextMonth = nextMonth.valueOf();
21560 var fillMonths = false;
21562 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21564 while(prevMonth.valueOf() <= nextMonth) {
21567 if (prevMonth.getUTCDay() === this.weekStart) {
21569 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21577 if(this.calendarWeeks){
21578 // ISO 8601: First week contains first thursday.
21579 // ISO also states week starts on Monday, but we can be more abstract here.
21581 // Start of current week: based on weekstart/current date
21582 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21583 // Thursday of this week
21584 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21585 // First Thursday of year, year from thursday
21586 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21587 // Calendar week: ms between thursdays, div ms per day, div 7 days
21588 calWeek = (th - yth) / 864e5 / 7 + 1;
21590 fillMonths.cn.push({
21598 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21600 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21603 if (this.todayHighlight &&
21604 prevMonth.getUTCFullYear() == today.getFullYear() &&
21605 prevMonth.getUTCMonth() == today.getMonth() &&
21606 prevMonth.getUTCDate() == today.getDate()) {
21607 clsName += ' today';
21610 if (currentDate && prevMonth.valueOf() === currentDate) {
21611 clsName += ' active';
21614 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21615 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21616 clsName += ' disabled';
21619 fillMonths.cn.push({
21621 cls: 'day ' + clsName,
21622 html: prevMonth.getDate()
21625 prevMonth.setDate(prevMonth.getDate()+1);
21628 var currentYear = this.date && this.date.getUTCFullYear();
21629 var currentMonth = this.date && this.date.getUTCMonth();
21631 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21633 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21634 v.removeClass('active');
21636 if(currentYear === year && k === currentMonth){
21637 v.addClass('active');
21640 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21641 v.addClass('disabled');
21647 year = parseInt(year/10, 10) * 10;
21649 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21651 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21654 for (var i = -1; i < 11; i++) {
21655 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21657 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21665 showMode: function(dir)
21668 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21671 Roo.each(this.picker().select('>div',true).elements, function(v){
21672 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21675 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21680 if(this.isInline) {
21684 this.picker().removeClass(['bottom', 'top']);
21686 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21688 * place to the top of element!
21692 this.picker().addClass('top');
21693 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21698 this.picker().addClass('bottom');
21700 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21703 parseDate : function(value)
21705 if(!value || value instanceof Date){
21708 var v = Date.parseDate(value, this.format);
21709 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21710 v = Date.parseDate(value, 'Y-m-d');
21712 if(!v && this.altFormats){
21713 if(!this.altFormatsArray){
21714 this.altFormatsArray = this.altFormats.split("|");
21716 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21717 v = Date.parseDate(value, this.altFormatsArray[i]);
21723 formatDate : function(date, fmt)
21725 return (!date || !(date instanceof Date)) ?
21726 date : date.dateFormat(fmt || this.format);
21729 onFocus : function()
21731 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21735 onBlur : function()
21737 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21739 var d = this.inputEl().getValue();
21746 showPopup : function()
21748 this.picker().show();
21752 this.fireEvent('showpopup', this, this.date);
21755 hidePopup : function()
21757 if(this.isInline) {
21760 this.picker().hide();
21761 this.viewMode = this.startViewMode;
21764 this.fireEvent('hidepopup', this, this.date);
21768 onMousedown: function(e)
21770 e.stopPropagation();
21771 e.preventDefault();
21776 Roo.bootstrap.DateField.superclass.keyup.call(this);
21780 setValue: function(v)
21782 if(this.fireEvent('beforeselect', this, v) !== false){
21783 var d = new Date(this.parseDate(v) ).clearTime();
21785 if(isNaN(d.getTime())){
21786 this.date = this.viewDate = '';
21787 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21791 v = this.formatDate(d);
21793 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21795 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21799 this.fireEvent('select', this, this.date);
21803 getValue: function()
21805 return this.formatDate(this.date);
21808 fireKey: function(e)
21810 if (!this.picker().isVisible()){
21811 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21817 var dateChanged = false,
21819 newDate, newViewDate;
21824 e.preventDefault();
21828 if (!this.keyboardNavigation) {
21831 dir = e.keyCode == 37 ? -1 : 1;
21834 newDate = this.moveYear(this.date, dir);
21835 newViewDate = this.moveYear(this.viewDate, dir);
21836 } else if (e.shiftKey){
21837 newDate = this.moveMonth(this.date, dir);
21838 newViewDate = this.moveMonth(this.viewDate, dir);
21840 newDate = new Date(this.date);
21841 newDate.setUTCDate(this.date.getUTCDate() + dir);
21842 newViewDate = new Date(this.viewDate);
21843 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21845 if (this.dateWithinRange(newDate)){
21846 this.date = newDate;
21847 this.viewDate = newViewDate;
21848 this.setValue(this.formatDate(this.date));
21850 e.preventDefault();
21851 dateChanged = true;
21856 if (!this.keyboardNavigation) {
21859 dir = e.keyCode == 38 ? -1 : 1;
21861 newDate = this.moveYear(this.date, dir);
21862 newViewDate = this.moveYear(this.viewDate, dir);
21863 } else if (e.shiftKey){
21864 newDate = this.moveMonth(this.date, dir);
21865 newViewDate = this.moveMonth(this.viewDate, dir);
21867 newDate = new Date(this.date);
21868 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21869 newViewDate = new Date(this.viewDate);
21870 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21872 if (this.dateWithinRange(newDate)){
21873 this.date = newDate;
21874 this.viewDate = newViewDate;
21875 this.setValue(this.formatDate(this.date));
21877 e.preventDefault();
21878 dateChanged = true;
21882 this.setValue(this.formatDate(this.date));
21884 e.preventDefault();
21887 this.setValue(this.formatDate(this.date));
21901 onClick: function(e)
21903 e.stopPropagation();
21904 e.preventDefault();
21906 var target = e.getTarget();
21908 if(target.nodeName.toLowerCase() === 'i'){
21909 target = Roo.get(target).dom.parentNode;
21912 var nodeName = target.nodeName;
21913 var className = target.className;
21914 var html = target.innerHTML;
21915 //Roo.log(nodeName);
21917 switch(nodeName.toLowerCase()) {
21919 switch(className) {
21925 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21926 switch(this.viewMode){
21928 this.viewDate = this.moveMonth(this.viewDate, dir);
21932 this.viewDate = this.moveYear(this.viewDate, dir);
21938 var date = new Date();
21939 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21941 this.setValue(this.formatDate(this.date));
21948 if (className.indexOf('disabled') < 0) {
21949 this.viewDate.setUTCDate(1);
21950 if (className.indexOf('month') > -1) {
21951 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21953 var year = parseInt(html, 10) || 0;
21954 this.viewDate.setUTCFullYear(year);
21958 if(this.singleMode){
21959 this.setValue(this.formatDate(this.viewDate));
21970 //Roo.log(className);
21971 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21972 var day = parseInt(html, 10) || 1;
21973 var year = (this.viewDate || new Date()).getUTCFullYear(),
21974 month = (this.viewDate || new Date()).getUTCMonth();
21976 if (className.indexOf('old') > -1) {
21983 } else if (className.indexOf('new') > -1) {
21991 //Roo.log([year,month,day]);
21992 this.date = this.UTCDate(year, month, day,0,0,0,0);
21993 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21995 //Roo.log(this.formatDate(this.date));
21996 this.setValue(this.formatDate(this.date));
22003 setStartDate: function(startDate)
22005 this.startDate = startDate || -Infinity;
22006 if (this.startDate !== -Infinity) {
22007 this.startDate = this.parseDate(this.startDate);
22010 this.updateNavArrows();
22013 setEndDate: function(endDate)
22015 this.endDate = endDate || Infinity;
22016 if (this.endDate !== Infinity) {
22017 this.endDate = this.parseDate(this.endDate);
22020 this.updateNavArrows();
22023 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22025 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22026 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22027 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22029 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22030 return parseInt(d, 10);
22033 this.updateNavArrows();
22036 updateNavArrows: function()
22038 if(this.singleMode){
22042 var d = new Date(this.viewDate),
22043 year = d.getUTCFullYear(),
22044 month = d.getUTCMonth();
22046 Roo.each(this.picker().select('.prev', true).elements, function(v){
22048 switch (this.viewMode) {
22051 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22057 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22064 Roo.each(this.picker().select('.next', true).elements, function(v){
22066 switch (this.viewMode) {
22069 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22075 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22083 moveMonth: function(date, dir)
22088 var new_date = new Date(date.valueOf()),
22089 day = new_date.getUTCDate(),
22090 month = new_date.getUTCMonth(),
22091 mag = Math.abs(dir),
22093 dir = dir > 0 ? 1 : -1;
22096 // If going back one month, make sure month is not current month
22097 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22099 return new_date.getUTCMonth() == month;
22101 // If going forward one month, make sure month is as expected
22102 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22104 return new_date.getUTCMonth() != new_month;
22106 new_month = month + dir;
22107 new_date.setUTCMonth(new_month);
22108 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22109 if (new_month < 0 || new_month > 11) {
22110 new_month = (new_month + 12) % 12;
22113 // For magnitudes >1, move one month at a time...
22114 for (var i=0; i<mag; i++) {
22115 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22116 new_date = this.moveMonth(new_date, dir);
22118 // ...then reset the day, keeping it in the new month
22119 new_month = new_date.getUTCMonth();
22120 new_date.setUTCDate(day);
22122 return new_month != new_date.getUTCMonth();
22125 // Common date-resetting loop -- if date is beyond end of month, make it
22128 new_date.setUTCDate(--day);
22129 new_date.setUTCMonth(new_month);
22134 moveYear: function(date, dir)
22136 return this.moveMonth(date, dir*12);
22139 dateWithinRange: function(date)
22141 return date >= this.startDate && date <= this.endDate;
22147 this.picker().remove();
22150 validateValue : function(value)
22152 if(this.getVisibilityEl().hasClass('hidden')){
22156 if(value.length < 1) {
22157 if(this.allowBlank){
22163 if(value.length < this.minLength){
22166 if(value.length > this.maxLength){
22170 var vt = Roo.form.VTypes;
22171 if(!vt[this.vtype](value, this)){
22175 if(typeof this.validator == "function"){
22176 var msg = this.validator(value);
22182 if(this.regex && !this.regex.test(value)){
22186 if(typeof(this.parseDate(value)) == 'undefined'){
22190 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22194 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22204 this.date = this.viewDate = '';
22206 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22211 Roo.apply(Roo.bootstrap.DateField, {
22222 html: '<i class="fa fa-arrow-left"/>'
22232 html: '<i class="fa fa-arrow-right"/>'
22274 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22275 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22276 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22277 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22278 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22291 navFnc: 'FullYear',
22296 navFnc: 'FullYear',
22301 Roo.apply(Roo.bootstrap.DateField, {
22305 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22309 cls: 'datepicker-days',
22313 cls: 'table-condensed',
22315 Roo.bootstrap.DateField.head,
22319 Roo.bootstrap.DateField.footer
22326 cls: 'datepicker-months',
22330 cls: 'table-condensed',
22332 Roo.bootstrap.DateField.head,
22333 Roo.bootstrap.DateField.content,
22334 Roo.bootstrap.DateField.footer
22341 cls: 'datepicker-years',
22345 cls: 'table-condensed',
22347 Roo.bootstrap.DateField.head,
22348 Roo.bootstrap.DateField.content,
22349 Roo.bootstrap.DateField.footer
22368 * @class Roo.bootstrap.TimeField
22369 * @extends Roo.bootstrap.Input
22370 * Bootstrap DateField class
22374 * Create a new TimeField
22375 * @param {Object} config The config object
22378 Roo.bootstrap.TimeField = function(config){
22379 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22383 * Fires when this field show.
22384 * @param {Roo.bootstrap.DateField} thisthis
22385 * @param {Mixed} date The date value
22390 * Fires when this field hide.
22391 * @param {Roo.bootstrap.DateField} this
22392 * @param {Mixed} date The date value
22397 * Fires when select a date.
22398 * @param {Roo.bootstrap.DateField} this
22399 * @param {Mixed} date The date value
22405 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22408 * @cfg {String} format
22409 * The default time format string which can be overriden for localization support. The format must be
22410 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22414 getAutoCreate : function()
22416 this.after = '<i class="fa far fa-clock"></i>';
22417 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22421 onRender: function(ct, position)
22424 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22426 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22428 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22430 this.pop = this.picker().select('>.datepicker-time',true).first();
22431 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22433 this.picker().on('mousedown', this.onMousedown, this);
22434 this.picker().on('click', this.onClick, this);
22436 this.picker().addClass('datepicker-dropdown');
22441 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22442 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22443 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22444 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22445 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22446 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22450 fireKey: function(e){
22451 if (!this.picker().isVisible()){
22452 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22458 e.preventDefault();
22466 this.onTogglePeriod();
22469 this.onIncrementMinutes();
22472 this.onDecrementMinutes();
22481 onClick: function(e) {
22482 e.stopPropagation();
22483 e.preventDefault();
22486 picker : function()
22488 return this.pickerEl;
22491 fillTime: function()
22493 var time = this.pop.select('tbody', true).first();
22495 time.dom.innerHTML = '';
22510 cls: 'hours-up fa fas fa-chevron-up'
22530 cls: 'minutes-up fa fas fa-chevron-up'
22551 cls: 'timepicker-hour',
22566 cls: 'timepicker-minute',
22581 cls: 'btn btn-primary period',
22603 cls: 'hours-down fa fas fa-chevron-down'
22623 cls: 'minutes-down fa fas fa-chevron-down'
22641 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22648 var hours = this.time.getHours();
22649 var minutes = this.time.getMinutes();
22662 hours = hours - 12;
22666 hours = '0' + hours;
22670 minutes = '0' + minutes;
22673 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22674 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22675 this.pop.select('button', true).first().dom.innerHTML = period;
22681 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22683 var cls = ['bottom'];
22685 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22692 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22696 //this.picker().setXY(20000,20000);
22697 this.picker().addClass(cls.join('-'));
22701 Roo.each(cls, function(c){
22706 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22707 //_this.picker().setTop(_this.inputEl().getHeight());
22711 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22713 //_this.picker().setTop(0 - _this.picker().getHeight());
22718 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22722 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22730 onFocus : function()
22732 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22736 onBlur : function()
22738 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22744 this.picker().show();
22749 this.fireEvent('show', this, this.date);
22754 this.picker().hide();
22757 this.fireEvent('hide', this, this.date);
22760 setTime : function()
22763 this.setValue(this.time.format(this.format));
22765 this.fireEvent('select', this, this.date);
22770 onMousedown: function(e){
22771 e.stopPropagation();
22772 e.preventDefault();
22775 onIncrementHours: function()
22777 Roo.log('onIncrementHours');
22778 this.time = this.time.add(Date.HOUR, 1);
22783 onDecrementHours: function()
22785 Roo.log('onDecrementHours');
22786 this.time = this.time.add(Date.HOUR, -1);
22790 onIncrementMinutes: function()
22792 Roo.log('onIncrementMinutes');
22793 this.time = this.time.add(Date.MINUTE, 1);
22797 onDecrementMinutes: function()
22799 Roo.log('onDecrementMinutes');
22800 this.time = this.time.add(Date.MINUTE, -1);
22804 onTogglePeriod: function()
22806 Roo.log('onTogglePeriod');
22807 this.time = this.time.add(Date.HOUR, 12);
22815 Roo.apply(Roo.bootstrap.TimeField, {
22819 cls: 'datepicker dropdown-menu',
22823 cls: 'datepicker-time',
22827 cls: 'table-condensed',
22856 cls: 'btn btn-info ok',
22884 * @class Roo.bootstrap.MonthField
22885 * @extends Roo.bootstrap.Input
22886 * Bootstrap MonthField class
22888 * @cfg {String} language default en
22891 * Create a new MonthField
22892 * @param {Object} config The config object
22895 Roo.bootstrap.MonthField = function(config){
22896 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22901 * Fires when this field show.
22902 * @param {Roo.bootstrap.MonthField} this
22903 * @param {Mixed} date The date value
22908 * Fires when this field hide.
22909 * @param {Roo.bootstrap.MonthField} this
22910 * @param {Mixed} date The date value
22915 * Fires when select a date.
22916 * @param {Roo.bootstrap.MonthField} this
22917 * @param {String} oldvalue The old value
22918 * @param {String} newvalue The new value
22924 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22926 onRender: function(ct, position)
22929 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22931 this.language = this.language || 'en';
22932 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22933 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22935 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22936 this.isInline = false;
22937 this.isInput = true;
22938 this.component = this.el.select('.add-on', true).first() || false;
22939 this.component = (this.component && this.component.length === 0) ? false : this.component;
22940 this.hasInput = this.component && this.inputEL().length;
22942 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22944 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22946 this.picker().on('mousedown', this.onMousedown, this);
22947 this.picker().on('click', this.onClick, this);
22949 this.picker().addClass('datepicker-dropdown');
22951 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22952 v.setStyle('width', '189px');
22959 if(this.isInline) {
22965 setValue: function(v, suppressEvent)
22967 var o = this.getValue();
22969 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22973 if(suppressEvent !== true){
22974 this.fireEvent('select', this, o, v);
22979 getValue: function()
22984 onClick: function(e)
22986 e.stopPropagation();
22987 e.preventDefault();
22989 var target = e.getTarget();
22991 if(target.nodeName.toLowerCase() === 'i'){
22992 target = Roo.get(target).dom.parentNode;
22995 var nodeName = target.nodeName;
22996 var className = target.className;
22997 var html = target.innerHTML;
22999 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23003 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23005 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23011 picker : function()
23013 return this.pickerEl;
23016 fillMonths: function()
23019 var months = this.picker().select('>.datepicker-months td', true).first();
23021 months.dom.innerHTML = '';
23027 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23030 months.createChild(month);
23039 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23040 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23043 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23044 e.removeClass('active');
23046 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23047 e.addClass('active');
23054 if(this.isInline) {
23058 this.picker().removeClass(['bottom', 'top']);
23060 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23062 * place to the top of element!
23066 this.picker().addClass('top');
23067 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23072 this.picker().addClass('bottom');
23074 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23077 onFocus : function()
23079 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23083 onBlur : function()
23085 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23087 var d = this.inputEl().getValue();
23096 this.picker().show();
23097 this.picker().select('>.datepicker-months', true).first().show();
23101 this.fireEvent('show', this, this.date);
23106 if(this.isInline) {
23109 this.picker().hide();
23110 this.fireEvent('hide', this, this.date);
23114 onMousedown: function(e)
23116 e.stopPropagation();
23117 e.preventDefault();
23122 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23126 fireKey: function(e)
23128 if (!this.picker().isVisible()){
23129 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23140 e.preventDefault();
23144 dir = e.keyCode == 37 ? -1 : 1;
23146 this.vIndex = this.vIndex + dir;
23148 if(this.vIndex < 0){
23152 if(this.vIndex > 11){
23156 if(isNaN(this.vIndex)){
23160 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23166 dir = e.keyCode == 38 ? -1 : 1;
23168 this.vIndex = this.vIndex + dir * 4;
23170 if(this.vIndex < 0){
23174 if(this.vIndex > 11){
23178 if(isNaN(this.vIndex)){
23182 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23187 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23188 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23192 e.preventDefault();
23195 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23196 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23212 this.picker().remove();
23217 Roo.apply(Roo.bootstrap.MonthField, {
23236 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23237 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23242 Roo.apply(Roo.bootstrap.MonthField, {
23246 cls: 'datepicker dropdown-menu roo-dynamic',
23250 cls: 'datepicker-months',
23254 cls: 'table-condensed',
23256 Roo.bootstrap.DateField.content
23276 * @class Roo.bootstrap.CheckBox
23277 * @extends Roo.bootstrap.Input
23278 * Bootstrap CheckBox class
23280 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23281 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23282 * @cfg {String} boxLabel The text that appears beside the checkbox
23283 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23284 * @cfg {Boolean} checked initnal the element
23285 * @cfg {Boolean} inline inline the element (default false)
23286 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23287 * @cfg {String} tooltip label tooltip
23290 * Create a new CheckBox
23291 * @param {Object} config The config object
23294 Roo.bootstrap.CheckBox = function(config){
23295 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23300 * Fires when the element is checked or unchecked.
23301 * @param {Roo.bootstrap.CheckBox} this This input
23302 * @param {Boolean} checked The new checked value
23307 * Fires when the element is click.
23308 * @param {Roo.bootstrap.CheckBox} this This input
23315 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23317 inputType: 'checkbox',
23326 // checkbox success does not make any sense really..
23331 getAutoCreate : function()
23333 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23339 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23342 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23348 type : this.inputType,
23349 value : this.inputValue,
23350 cls : 'roo-' + this.inputType, //'form-box',
23351 placeholder : this.placeholder || ''
23355 if(this.inputType != 'radio'){
23359 cls : 'roo-hidden-value',
23360 value : this.checked ? this.inputValue : this.valueOff
23365 if (this.weight) { // Validity check?
23366 cfg.cls += " " + this.inputType + "-" + this.weight;
23369 if (this.disabled) {
23370 input.disabled=true;
23374 input.checked = this.checked;
23379 input.name = this.name;
23381 if(this.inputType != 'radio'){
23382 hidden.name = this.name;
23383 input.name = '_hidden_' + this.name;
23388 input.cls += ' input-' + this.size;
23393 ['xs','sm','md','lg'].map(function(size){
23394 if (settings[size]) {
23395 cfg.cls += ' col-' + size + '-' + settings[size];
23399 var inputblock = input;
23401 if (this.before || this.after) {
23404 cls : 'input-group',
23409 inputblock.cn.push({
23411 cls : 'input-group-addon',
23416 inputblock.cn.push(input);
23418 if(this.inputType != 'radio'){
23419 inputblock.cn.push(hidden);
23423 inputblock.cn.push({
23425 cls : 'input-group-addon',
23431 var boxLabelCfg = false;
23437 //'for': id, // box label is handled by onclick - so no for...
23439 html: this.boxLabel
23442 boxLabelCfg.tooltip = this.tooltip;
23448 if (align ==='left' && this.fieldLabel.length) {
23449 // Roo.log("left and has label");
23454 cls : 'control-label',
23455 html : this.fieldLabel
23466 cfg.cn[1].cn.push(boxLabelCfg);
23469 if(this.labelWidth > 12){
23470 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23473 if(this.labelWidth < 13 && this.labelmd == 0){
23474 this.labelmd = this.labelWidth;
23477 if(this.labellg > 0){
23478 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23479 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23482 if(this.labelmd > 0){
23483 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23484 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23487 if(this.labelsm > 0){
23488 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23489 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23492 if(this.labelxs > 0){
23493 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23494 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23497 } else if ( this.fieldLabel.length) {
23498 // Roo.log(" label");
23502 tag: this.boxLabel ? 'span' : 'label',
23504 cls: 'control-label box-input-label',
23505 //cls : 'input-group-addon',
23506 html : this.fieldLabel
23513 cfg.cn.push(boxLabelCfg);
23518 // Roo.log(" no label && no align");
23519 cfg.cn = [ inputblock ] ;
23521 cfg.cn.push(boxLabelCfg);
23529 if(this.inputType != 'radio'){
23530 cfg.cn.push(hidden);
23538 * return the real input element.
23540 inputEl: function ()
23542 return this.el.select('input.roo-' + this.inputType,true).first();
23544 hiddenEl: function ()
23546 return this.el.select('input.roo-hidden-value',true).first();
23549 labelEl: function()
23551 return this.el.select('label.control-label',true).first();
23553 /* depricated... */
23557 return this.labelEl();
23560 boxLabelEl: function()
23562 return this.el.select('label.box-label',true).first();
23565 initEvents : function()
23567 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23569 this.inputEl().on('click', this.onClick, this);
23571 if (this.boxLabel) {
23572 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23575 this.startValue = this.getValue();
23578 Roo.bootstrap.CheckBox.register(this);
23582 onClick : function(e)
23584 if(this.fireEvent('click', this, e) !== false){
23585 this.setChecked(!this.checked);
23590 setChecked : function(state,suppressEvent)
23592 this.startValue = this.getValue();
23594 if(this.inputType == 'radio'){
23596 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23597 e.dom.checked = false;
23600 this.inputEl().dom.checked = true;
23602 this.inputEl().dom.value = this.inputValue;
23604 if(suppressEvent !== true){
23605 this.fireEvent('check', this, true);
23613 this.checked = state;
23615 this.inputEl().dom.checked = state;
23618 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23620 if(suppressEvent !== true){
23621 this.fireEvent('check', this, state);
23627 getValue : function()
23629 if(this.inputType == 'radio'){
23630 return this.getGroupValue();
23633 return this.hiddenEl().dom.value;
23637 getGroupValue : function()
23639 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23643 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23646 setValue : function(v,suppressEvent)
23648 if(this.inputType == 'radio'){
23649 this.setGroupValue(v, suppressEvent);
23653 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23658 setGroupValue : function(v, suppressEvent)
23660 this.startValue = this.getValue();
23662 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23663 e.dom.checked = false;
23665 if(e.dom.value == v){
23666 e.dom.checked = true;
23670 if(suppressEvent !== true){
23671 this.fireEvent('check', this, true);
23679 validate : function()
23681 if(this.getVisibilityEl().hasClass('hidden')){
23687 (this.inputType == 'radio' && this.validateRadio()) ||
23688 (this.inputType == 'checkbox' && this.validateCheckbox())
23694 this.markInvalid();
23698 validateRadio : function()
23700 if(this.getVisibilityEl().hasClass('hidden')){
23704 if(this.allowBlank){
23710 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23711 if(!e.dom.checked){
23723 validateCheckbox : function()
23726 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23727 //return (this.getValue() == this.inputValue) ? true : false;
23730 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23738 for(var i in group){
23739 if(group[i].el.isVisible(true)){
23747 for(var i in group){
23752 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23759 * Mark this field as valid
23761 markValid : function()
23765 this.fireEvent('valid', this);
23767 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23770 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23777 if(this.inputType == 'radio'){
23778 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23779 var fg = e.findParent('.form-group', false, true);
23780 if (Roo.bootstrap.version == 3) {
23781 fg.removeClass([_this.invalidClass, _this.validClass]);
23782 fg.addClass(_this.validClass);
23784 fg.removeClass(['is-valid', 'is-invalid']);
23785 fg.addClass('is-valid');
23793 var fg = this.el.findParent('.form-group', false, true);
23794 if (Roo.bootstrap.version == 3) {
23795 fg.removeClass([this.invalidClass, this.validClass]);
23796 fg.addClass(this.validClass);
23798 fg.removeClass(['is-valid', 'is-invalid']);
23799 fg.addClass('is-valid');
23804 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23810 for(var i in group){
23811 var fg = group[i].el.findParent('.form-group', false, true);
23812 if (Roo.bootstrap.version == 3) {
23813 fg.removeClass([this.invalidClass, this.validClass]);
23814 fg.addClass(this.validClass);
23816 fg.removeClass(['is-valid', 'is-invalid']);
23817 fg.addClass('is-valid');
23823 * Mark this field as invalid
23824 * @param {String} msg The validation message
23826 markInvalid : function(msg)
23828 if(this.allowBlank){
23834 this.fireEvent('invalid', this, msg);
23836 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23839 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23843 label.markInvalid();
23846 if(this.inputType == 'radio'){
23848 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23849 var fg = e.findParent('.form-group', false, true);
23850 if (Roo.bootstrap.version == 3) {
23851 fg.removeClass([_this.invalidClass, _this.validClass]);
23852 fg.addClass(_this.invalidClass);
23854 fg.removeClass(['is-invalid', 'is-valid']);
23855 fg.addClass('is-invalid');
23863 var fg = this.el.findParent('.form-group', false, true);
23864 if (Roo.bootstrap.version == 3) {
23865 fg.removeClass([_this.invalidClass, _this.validClass]);
23866 fg.addClass(_this.invalidClass);
23868 fg.removeClass(['is-invalid', 'is-valid']);
23869 fg.addClass('is-invalid');
23874 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23880 for(var i in group){
23881 var fg = group[i].el.findParent('.form-group', false, true);
23882 if (Roo.bootstrap.version == 3) {
23883 fg.removeClass([_this.invalidClass, _this.validClass]);
23884 fg.addClass(_this.invalidClass);
23886 fg.removeClass(['is-invalid', 'is-valid']);
23887 fg.addClass('is-invalid');
23893 clearInvalid : function()
23895 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23897 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23899 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23901 if (label && label.iconEl) {
23902 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23903 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23907 disable : function()
23909 if(this.inputType != 'radio'){
23910 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23917 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23918 _this.getActionEl().addClass(this.disabledClass);
23919 e.dom.disabled = true;
23923 this.disabled = true;
23924 this.fireEvent("disable", this);
23928 enable : function()
23930 if(this.inputType != 'radio'){
23931 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23938 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23939 _this.getActionEl().removeClass(this.disabledClass);
23940 e.dom.disabled = false;
23944 this.disabled = false;
23945 this.fireEvent("enable", this);
23949 setBoxLabel : function(v)
23954 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23960 Roo.apply(Roo.bootstrap.CheckBox, {
23965 * register a CheckBox Group
23966 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23968 register : function(checkbox)
23970 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23971 this.groups[checkbox.groupId] = {};
23974 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23978 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23982 * fetch a CheckBox Group based on the group ID
23983 * @param {string} the group ID
23984 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23986 get: function(groupId) {
23987 if (typeof(this.groups[groupId]) == 'undefined') {
23991 return this.groups[groupId] ;
24004 * @class Roo.bootstrap.Radio
24005 * @extends Roo.bootstrap.Component
24006 * Bootstrap Radio class
24007 * @cfg {String} boxLabel - the label associated
24008 * @cfg {String} value - the value of radio
24011 * Create a new Radio
24012 * @param {Object} config The config object
24014 Roo.bootstrap.Radio = function(config){
24015 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24019 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24025 getAutoCreate : function()
24029 cls : 'form-group radio',
24034 html : this.boxLabel
24042 initEvents : function()
24044 this.parent().register(this);
24046 this.el.on('click', this.onClick, this);
24050 onClick : function(e)
24052 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24053 this.setChecked(true);
24057 setChecked : function(state, suppressEvent)
24059 this.parent().setValue(this.value, suppressEvent);
24063 setBoxLabel : function(v)
24068 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24083 * @class Roo.bootstrap.SecurePass
24084 * @extends Roo.bootstrap.Input
24085 * Bootstrap SecurePass class
24089 * Create a new SecurePass
24090 * @param {Object} config The config object
24093 Roo.bootstrap.SecurePass = function (config) {
24094 // these go here, so the translation tool can replace them..
24096 PwdEmpty: "Please type a password, and then retype it to confirm.",
24097 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24098 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24099 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24100 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24101 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24102 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24103 TooWeak: "Your password is Too Weak."
24105 this.meterLabel = "Password strength:";
24106 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24107 this.meterClass = [
24108 "roo-password-meter-tooweak",
24109 "roo-password-meter-weak",
24110 "roo-password-meter-medium",
24111 "roo-password-meter-strong",
24112 "roo-password-meter-grey"
24117 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24120 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24122 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24124 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24125 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24126 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24127 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24128 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24129 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24130 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24140 * @cfg {String/Object} Label for the strength meter (defaults to
24141 * 'Password strength:')
24146 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24147 * ['Weak', 'Medium', 'Strong'])
24150 pwdStrengths: false,
24163 initEvents: function ()
24165 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24167 if (this.el.is('input[type=password]') && Roo.isSafari) {
24168 this.el.on('keydown', this.SafariOnKeyDown, this);
24171 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24174 onRender: function (ct, position)
24176 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24177 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24178 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24180 this.trigger.createChild({
24185 cls: 'roo-password-meter-grey col-xs-12',
24188 //width: this.meterWidth + 'px'
24192 cls: 'roo-password-meter-text'
24198 if (this.hideTrigger) {
24199 this.trigger.setDisplayed(false);
24201 this.setSize(this.width || '', this.height || '');
24204 onDestroy: function ()
24206 if (this.trigger) {
24207 this.trigger.removeAllListeners();
24208 this.trigger.remove();
24211 this.wrap.remove();
24213 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24216 checkStrength: function ()
24218 var pwd = this.inputEl().getValue();
24219 if (pwd == this._lastPwd) {
24224 if (this.ClientSideStrongPassword(pwd)) {
24226 } else if (this.ClientSideMediumPassword(pwd)) {
24228 } else if (this.ClientSideWeakPassword(pwd)) {
24234 Roo.log('strength1: ' + strength);
24236 //var pm = this.trigger.child('div/div/div').dom;
24237 var pm = this.trigger.child('div/div');
24238 pm.removeClass(this.meterClass);
24239 pm.addClass(this.meterClass[strength]);
24242 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24244 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24246 this._lastPwd = pwd;
24250 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24252 this._lastPwd = '';
24254 var pm = this.trigger.child('div/div');
24255 pm.removeClass(this.meterClass);
24256 pm.addClass('roo-password-meter-grey');
24259 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24262 this.inputEl().dom.type='password';
24265 validateValue: function (value)
24267 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24270 if (value.length == 0) {
24271 if (this.allowBlank) {
24272 this.clearInvalid();
24276 this.markInvalid(this.errors.PwdEmpty);
24277 this.errorMsg = this.errors.PwdEmpty;
24285 if (!value.match(/[\x21-\x7e]+/)) {
24286 this.markInvalid(this.errors.PwdBadChar);
24287 this.errorMsg = this.errors.PwdBadChar;
24290 if (value.length < 6) {
24291 this.markInvalid(this.errors.PwdShort);
24292 this.errorMsg = this.errors.PwdShort;
24295 if (value.length > 16) {
24296 this.markInvalid(this.errors.PwdLong);
24297 this.errorMsg = this.errors.PwdLong;
24301 if (this.ClientSideStrongPassword(value)) {
24303 } else if (this.ClientSideMediumPassword(value)) {
24305 } else if (this.ClientSideWeakPassword(value)) {
24312 if (strength < 2) {
24313 //this.markInvalid(this.errors.TooWeak);
24314 this.errorMsg = this.errors.TooWeak;
24319 console.log('strength2: ' + strength);
24321 //var pm = this.trigger.child('div/div/div').dom;
24323 var pm = this.trigger.child('div/div');
24324 pm.removeClass(this.meterClass);
24325 pm.addClass(this.meterClass[strength]);
24327 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24329 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24331 this.errorMsg = '';
24335 CharacterSetChecks: function (type)
24338 this.fResult = false;
24341 isctype: function (character, type)
24344 case this.kCapitalLetter:
24345 if (character >= 'A' && character <= 'Z') {
24350 case this.kSmallLetter:
24351 if (character >= 'a' && character <= 'z') {
24357 if (character >= '0' && character <= '9') {
24362 case this.kPunctuation:
24363 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24374 IsLongEnough: function (pwd, size)
24376 return !(pwd == null || isNaN(size) || pwd.length < size);
24379 SpansEnoughCharacterSets: function (word, nb)
24381 if (!this.IsLongEnough(word, nb))
24386 var characterSetChecks = new Array(
24387 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24388 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24391 for (var index = 0; index < word.length; ++index) {
24392 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24393 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24394 characterSetChecks[nCharSet].fResult = true;
24401 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24402 if (characterSetChecks[nCharSet].fResult) {
24407 if (nCharSets < nb) {
24413 ClientSideStrongPassword: function (pwd)
24415 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24418 ClientSideMediumPassword: function (pwd)
24420 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24423 ClientSideWeakPassword: function (pwd)
24425 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24428 })//<script type="text/javascript">
24431 * Based Ext JS Library 1.1.1
24432 * Copyright(c) 2006-2007, Ext JS, LLC.
24438 * @class Roo.HtmlEditorCore
24439 * @extends Roo.Component
24440 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24442 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24445 Roo.HtmlEditorCore = function(config){
24448 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24453 * @event initialize
24454 * Fires when the editor is fully initialized (including the iframe)
24455 * @param {Roo.HtmlEditorCore} this
24460 * Fires when the editor is first receives the focus. Any insertion must wait
24461 * until after this event.
24462 * @param {Roo.HtmlEditorCore} this
24466 * @event beforesync
24467 * Fires before the textarea is updated with content from the editor iframe. Return false
24468 * to cancel the sync.
24469 * @param {Roo.HtmlEditorCore} this
24470 * @param {String} html
24474 * @event beforepush
24475 * Fires before the iframe editor is updated with content from the textarea. Return false
24476 * to cancel the push.
24477 * @param {Roo.HtmlEditorCore} this
24478 * @param {String} html
24483 * Fires when the textarea is updated with content from the editor iframe.
24484 * @param {Roo.HtmlEditorCore} this
24485 * @param {String} html
24490 * Fires when the iframe editor is updated with content from the textarea.
24491 * @param {Roo.HtmlEditorCore} this
24492 * @param {String} html
24497 * @event editorevent
24498 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24499 * @param {Roo.HtmlEditorCore} this
24505 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24507 // defaults : white / black...
24508 this.applyBlacklists();
24515 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24519 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24525 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24530 * @cfg {Number} height (in pixels)
24534 * @cfg {Number} width (in pixels)
24539 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24542 stylesheets: false,
24547 // private properties
24548 validationEvent : false,
24550 initialized : false,
24552 sourceEditMode : false,
24553 onFocus : Roo.emptyFn,
24555 hideMode:'offsets',
24559 // blacklist + whitelisted elements..
24566 * Protected method that will not generally be called directly. It
24567 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24568 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24570 getDocMarkup : function(){
24574 // inherit styels from page...??
24575 if (this.stylesheets === false) {
24577 Roo.get(document.head).select('style').each(function(node) {
24578 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24581 Roo.get(document.head).select('link').each(function(node) {
24582 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24585 } else if (!this.stylesheets.length) {
24587 st = '<style type="text/css">' +
24588 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24591 for (var i in this.stylesheets) {
24592 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24597 st += '<style type="text/css">' +
24598 'IMG { cursor: pointer } ' +
24601 var cls = 'roo-htmleditor-body';
24603 if(this.bodyCls.length){
24604 cls += ' ' + this.bodyCls;
24607 return '<html><head>' + st +
24608 //<style type="text/css">' +
24609 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24611 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24615 onRender : function(ct, position)
24618 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24619 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24622 this.el.dom.style.border = '0 none';
24623 this.el.dom.setAttribute('tabIndex', -1);
24624 this.el.addClass('x-hidden hide');
24628 if(Roo.isIE){ // fix IE 1px bogus margin
24629 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24633 this.frameId = Roo.id();
24637 var iframe = this.owner.wrap.createChild({
24639 cls: 'form-control', // bootstrap..
24641 name: this.frameId,
24642 frameBorder : 'no',
24643 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24648 this.iframe = iframe.dom;
24650 this.assignDocWin();
24652 this.doc.designMode = 'on';
24655 this.doc.write(this.getDocMarkup());
24659 var task = { // must defer to wait for browser to be ready
24661 //console.log("run task?" + this.doc.readyState);
24662 this.assignDocWin();
24663 if(this.doc.body || this.doc.readyState == 'complete'){
24665 this.doc.designMode="on";
24669 Roo.TaskMgr.stop(task);
24670 this.initEditor.defer(10, this);
24677 Roo.TaskMgr.start(task);
24682 onResize : function(w, h)
24684 Roo.log('resize: ' +w + ',' + h );
24685 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24689 if(typeof w == 'number'){
24691 this.iframe.style.width = w + 'px';
24693 if(typeof h == 'number'){
24695 this.iframe.style.height = h + 'px';
24697 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24704 * Toggles the editor between standard and source edit mode.
24705 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24707 toggleSourceEdit : function(sourceEditMode){
24709 this.sourceEditMode = sourceEditMode === true;
24711 if(this.sourceEditMode){
24713 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24716 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24717 //this.iframe.className = '';
24720 //this.setSize(this.owner.wrap.getSize());
24721 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24728 * Protected method that will not generally be called directly. If you need/want
24729 * custom HTML cleanup, this is the method you should override.
24730 * @param {String} html The HTML to be cleaned
24731 * return {String} The cleaned HTML
24733 cleanHtml : function(html){
24734 html = String(html);
24735 if(html.length > 5){
24736 if(Roo.isSafari){ // strip safari nonsense
24737 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24740 if(html == ' '){
24747 * HTML Editor -> Textarea
24748 * Protected method that will not generally be called directly. Syncs the contents
24749 * of the editor iframe with the textarea.
24751 syncValue : function(){
24752 if(this.initialized){
24753 var bd = (this.doc.body || this.doc.documentElement);
24754 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24755 var html = bd.innerHTML;
24757 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24758 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24760 html = '<div style="'+m[0]+'">' + html + '</div>';
24763 html = this.cleanHtml(html);
24764 // fix up the special chars.. normaly like back quotes in word...
24765 // however we do not want to do this with chinese..
24766 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24768 var cc = match.charCodeAt();
24770 // Get the character value, handling surrogate pairs
24771 if (match.length == 2) {
24772 // It's a surrogate pair, calculate the Unicode code point
24773 var high = match.charCodeAt(0) - 0xD800;
24774 var low = match.charCodeAt(1) - 0xDC00;
24775 cc = (high * 0x400) + low + 0x10000;
24777 (cc >= 0x4E00 && cc < 0xA000 ) ||
24778 (cc >= 0x3400 && cc < 0x4E00 ) ||
24779 (cc >= 0xf900 && cc < 0xfb00 )
24784 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24785 return "&#" + cc + ";";
24792 if(this.owner.fireEvent('beforesync', this, html) !== false){
24793 this.el.dom.value = html;
24794 this.owner.fireEvent('sync', this, html);
24800 * Protected method that will not generally be called directly. Pushes the value of the textarea
24801 * into the iframe editor.
24803 pushValue : function(){
24804 if(this.initialized){
24805 var v = this.el.dom.value.trim();
24807 // if(v.length < 1){
24811 if(this.owner.fireEvent('beforepush', this, v) !== false){
24812 var d = (this.doc.body || this.doc.documentElement);
24814 this.cleanUpPaste();
24815 this.el.dom.value = d.innerHTML;
24816 this.owner.fireEvent('push', this, v);
24822 deferFocus : function(){
24823 this.focus.defer(10, this);
24827 focus : function(){
24828 if(this.win && !this.sourceEditMode){
24835 assignDocWin: function()
24837 var iframe = this.iframe;
24840 this.doc = iframe.contentWindow.document;
24841 this.win = iframe.contentWindow;
24843 // if (!Roo.get(this.frameId)) {
24846 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24847 // this.win = Roo.get(this.frameId).dom.contentWindow;
24849 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24853 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24854 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24859 initEditor : function(){
24860 //console.log("INIT EDITOR");
24861 this.assignDocWin();
24865 this.doc.designMode="on";
24867 this.doc.write(this.getDocMarkup());
24870 var dbody = (this.doc.body || this.doc.documentElement);
24871 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24872 // this copies styles from the containing element into thsi one..
24873 // not sure why we need all of this..
24874 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24876 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24877 //ss['background-attachment'] = 'fixed'; // w3c
24878 dbody.bgProperties = 'fixed'; // ie
24879 //Roo.DomHelper.applyStyles(dbody, ss);
24880 Roo.EventManager.on(this.doc, {
24881 //'mousedown': this.onEditorEvent,
24882 'mouseup': this.onEditorEvent,
24883 'dblclick': this.onEditorEvent,
24884 'click': this.onEditorEvent,
24885 'keyup': this.onEditorEvent,
24890 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24892 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24893 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24895 this.initialized = true;
24897 this.owner.fireEvent('initialize', this);
24902 onDestroy : function(){
24908 //for (var i =0; i < this.toolbars.length;i++) {
24909 // // fixme - ask toolbars for heights?
24910 // this.toolbars[i].onDestroy();
24913 //this.wrap.dom.innerHTML = '';
24914 //this.wrap.remove();
24919 onFirstFocus : function(){
24921 this.assignDocWin();
24924 this.activated = true;
24927 if(Roo.isGecko){ // prevent silly gecko errors
24929 var s = this.win.getSelection();
24930 if(!s.focusNode || s.focusNode.nodeType != 3){
24931 var r = s.getRangeAt(0);
24932 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24937 this.execCmd('useCSS', true);
24938 this.execCmd('styleWithCSS', false);
24941 this.owner.fireEvent('activate', this);
24945 adjustFont: function(btn){
24946 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24947 //if(Roo.isSafari){ // safari
24950 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24951 if(Roo.isSafari){ // safari
24952 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24953 v = (v < 10) ? 10 : v;
24954 v = (v > 48) ? 48 : v;
24955 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24960 v = Math.max(1, v+adjust);
24962 this.execCmd('FontSize', v );
24965 onEditorEvent : function(e)
24967 this.owner.fireEvent('editorevent', this, e);
24968 // this.updateToolbar();
24969 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24972 insertTag : function(tg)
24974 // could be a bit smarter... -> wrap the current selected tRoo..
24975 if (tg.toLowerCase() == 'span' ||
24976 tg.toLowerCase() == 'code' ||
24977 tg.toLowerCase() == 'sup' ||
24978 tg.toLowerCase() == 'sub'
24981 range = this.createRange(this.getSelection());
24982 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24983 wrappingNode.appendChild(range.extractContents());
24984 range.insertNode(wrappingNode);
24991 this.execCmd("formatblock", tg);
24995 insertText : function(txt)
24999 var range = this.createRange();
25000 range.deleteContents();
25001 //alert(Sender.getAttribute('label'));
25003 range.insertNode(this.doc.createTextNode(txt));
25009 * Executes a Midas editor command on the editor document and performs necessary focus and
25010 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25011 * @param {String} cmd The Midas command
25012 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25014 relayCmd : function(cmd, value){
25016 this.execCmd(cmd, value);
25017 this.owner.fireEvent('editorevent', this);
25018 //this.updateToolbar();
25019 this.owner.deferFocus();
25023 * Executes a Midas editor command directly on the editor document.
25024 * For visual commands, you should use {@link #relayCmd} instead.
25025 * <b>This should only be called after the editor is initialized.</b>
25026 * @param {String} cmd The Midas command
25027 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25029 execCmd : function(cmd, value){
25030 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25037 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25039 * @param {String} text | dom node..
25041 insertAtCursor : function(text)
25044 if(!this.activated){
25050 var r = this.doc.selection.createRange();
25061 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25065 // from jquery ui (MIT licenced)
25067 var win = this.win;
25069 if (win.getSelection && win.getSelection().getRangeAt) {
25070 range = win.getSelection().getRangeAt(0);
25071 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25072 range.insertNode(node);
25073 } else if (win.document.selection && win.document.selection.createRange) {
25074 // no firefox support
25075 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25076 win.document.selection.createRange().pasteHTML(txt);
25078 // no firefox support
25079 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25080 this.execCmd('InsertHTML', txt);
25089 mozKeyPress : function(e){
25091 var c = e.getCharCode(), cmd;
25094 c = String.fromCharCode(c).toLowerCase();
25108 this.cleanUpPaste.defer(100, this);
25116 e.preventDefault();
25124 fixKeys : function(){ // load time branching for fastest keydown performance
25126 return function(e){
25127 var k = e.getKey(), r;
25130 r = this.doc.selection.createRange();
25133 r.pasteHTML('    ');
25140 r = this.doc.selection.createRange();
25142 var target = r.parentElement();
25143 if(!target || target.tagName.toLowerCase() != 'li'){
25145 r.pasteHTML('<br />');
25151 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25152 this.cleanUpPaste.defer(100, this);
25158 }else if(Roo.isOpera){
25159 return function(e){
25160 var k = e.getKey();
25164 this.execCmd('InsertHTML','    ');
25167 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25168 this.cleanUpPaste.defer(100, this);
25173 }else if(Roo.isSafari){
25174 return function(e){
25175 var k = e.getKey();
25179 this.execCmd('InsertText','\t');
25183 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25184 this.cleanUpPaste.defer(100, this);
25192 getAllAncestors: function()
25194 var p = this.getSelectedNode();
25197 a.push(p); // push blank onto stack..
25198 p = this.getParentElement();
25202 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25206 a.push(this.doc.body);
25210 lastSelNode : false,
25213 getSelection : function()
25215 this.assignDocWin();
25216 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25219 getSelectedNode: function()
25221 // this may only work on Gecko!!!
25223 // should we cache this!!!!
25228 var range = this.createRange(this.getSelection()).cloneRange();
25231 var parent = range.parentElement();
25233 var testRange = range.duplicate();
25234 testRange.moveToElementText(parent);
25235 if (testRange.inRange(range)) {
25238 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25241 parent = parent.parentElement;
25246 // is ancestor a text element.
25247 var ac = range.commonAncestorContainer;
25248 if (ac.nodeType == 3) {
25249 ac = ac.parentNode;
25252 var ar = ac.childNodes;
25255 var other_nodes = [];
25256 var has_other_nodes = false;
25257 for (var i=0;i<ar.length;i++) {
25258 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25261 // fullly contained node.
25263 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25268 // probably selected..
25269 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25270 other_nodes.push(ar[i]);
25274 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25279 has_other_nodes = true;
25281 if (!nodes.length && other_nodes.length) {
25282 nodes= other_nodes;
25284 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25290 createRange: function(sel)
25292 // this has strange effects when using with
25293 // top toolbar - not sure if it's a great idea.
25294 //this.editor.contentWindow.focus();
25295 if (typeof sel != "undefined") {
25297 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25299 return this.doc.createRange();
25302 return this.doc.createRange();
25305 getParentElement: function()
25308 this.assignDocWin();
25309 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25311 var range = this.createRange(sel);
25314 var p = range.commonAncestorContainer;
25315 while (p.nodeType == 3) { // text node
25326 * Range intersection.. the hard stuff...
25330 * [ -- selected range --- ]
25334 * if end is before start or hits it. fail.
25335 * if start is after end or hits it fail.
25337 * if either hits (but other is outside. - then it's not
25343 // @see http://www.thismuchiknow.co.uk/?p=64.
25344 rangeIntersectsNode : function(range, node)
25346 var nodeRange = node.ownerDocument.createRange();
25348 nodeRange.selectNode(node);
25350 nodeRange.selectNodeContents(node);
25353 var rangeStartRange = range.cloneRange();
25354 rangeStartRange.collapse(true);
25356 var rangeEndRange = range.cloneRange();
25357 rangeEndRange.collapse(false);
25359 var nodeStartRange = nodeRange.cloneRange();
25360 nodeStartRange.collapse(true);
25362 var nodeEndRange = nodeRange.cloneRange();
25363 nodeEndRange.collapse(false);
25365 return rangeStartRange.compareBoundaryPoints(
25366 Range.START_TO_START, nodeEndRange) == -1 &&
25367 rangeEndRange.compareBoundaryPoints(
25368 Range.START_TO_START, nodeStartRange) == 1;
25372 rangeCompareNode : function(range, node)
25374 var nodeRange = node.ownerDocument.createRange();
25376 nodeRange.selectNode(node);
25378 nodeRange.selectNodeContents(node);
25382 range.collapse(true);
25384 nodeRange.collapse(true);
25386 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25387 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25389 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25391 var nodeIsBefore = ss == 1;
25392 var nodeIsAfter = ee == -1;
25394 if (nodeIsBefore && nodeIsAfter) {
25397 if (!nodeIsBefore && nodeIsAfter) {
25398 return 1; //right trailed.
25401 if (nodeIsBefore && !nodeIsAfter) {
25402 return 2; // left trailed.
25408 // private? - in a new class?
25409 cleanUpPaste : function()
25411 // cleans up the whole document..
25412 Roo.log('cleanuppaste');
25414 this.cleanUpChildren(this.doc.body);
25415 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25416 if (clean != this.doc.body.innerHTML) {
25417 this.doc.body.innerHTML = clean;
25422 cleanWordChars : function(input) {// change the chars to hex code
25423 var he = Roo.HtmlEditorCore;
25425 var output = input;
25426 Roo.each(he.swapCodes, function(sw) {
25427 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25429 output = output.replace(swapper, sw[1]);
25436 cleanUpChildren : function (n)
25438 if (!n.childNodes.length) {
25441 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25442 this.cleanUpChild(n.childNodes[i]);
25449 cleanUpChild : function (node)
25452 //console.log(node);
25453 if (node.nodeName == "#text") {
25454 // clean up silly Windows -- stuff?
25457 if (node.nodeName == "#comment") {
25458 node.parentNode.removeChild(node);
25459 // clean up silly Windows -- stuff?
25462 var lcname = node.tagName.toLowerCase();
25463 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25464 // whitelist of tags..
25466 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25468 node.parentNode.removeChild(node);
25473 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25475 // spans with no attributes - just remove them..
25476 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25477 remove_keep_children = true;
25480 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25481 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25483 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25484 // remove_keep_children = true;
25487 if (remove_keep_children) {
25488 this.cleanUpChildren(node);
25489 // inserts everything just before this node...
25490 while (node.childNodes.length) {
25491 var cn = node.childNodes[0];
25492 node.removeChild(cn);
25493 node.parentNode.insertBefore(cn, node);
25495 node.parentNode.removeChild(node);
25499 if (!node.attributes || !node.attributes.length) {
25504 this.cleanUpChildren(node);
25508 function cleanAttr(n,v)
25511 if (v.match(/^\./) || v.match(/^\//)) {
25514 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25517 if (v.match(/^#/)) {
25520 if (v.match(/^\{/)) { // allow template editing.
25523 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25524 node.removeAttribute(n);
25528 var cwhite = this.cwhite;
25529 var cblack = this.cblack;
25531 function cleanStyle(n,v)
25533 if (v.match(/expression/)) { //XSS?? should we even bother..
25534 node.removeAttribute(n);
25538 var parts = v.split(/;/);
25541 Roo.each(parts, function(p) {
25542 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25546 var l = p.split(':').shift().replace(/\s+/g,'');
25547 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25549 if ( cwhite.length && cblack.indexOf(l) > -1) {
25550 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25551 //node.removeAttribute(n);
25555 // only allow 'c whitelisted system attributes'
25556 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25557 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25558 //node.removeAttribute(n);
25568 if (clean.length) {
25569 node.setAttribute(n, clean.join(';'));
25571 node.removeAttribute(n);
25577 for (var i = node.attributes.length-1; i > -1 ; i--) {
25578 var a = node.attributes[i];
25581 if (a.name.toLowerCase().substr(0,2)=='on') {
25582 node.removeAttribute(a.name);
25585 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25586 node.removeAttribute(a.name);
25589 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25590 cleanAttr(a.name,a.value); // fixme..
25593 if (a.name == 'style') {
25594 cleanStyle(a.name,a.value);
25597 /// clean up MS crap..
25598 // tecnically this should be a list of valid class'es..
25601 if (a.name == 'class') {
25602 if (a.value.match(/^Mso/)) {
25603 node.removeAttribute('class');
25606 if (a.value.match(/^body$/)) {
25607 node.removeAttribute('class');
25618 this.cleanUpChildren(node);
25624 * Clean up MS wordisms...
25626 cleanWord : function(node)
25629 this.cleanWord(this.doc.body);
25634 node.nodeName == 'SPAN' &&
25635 !node.hasAttributes() &&
25636 node.childNodes.length == 1 &&
25637 node.firstChild.nodeName == "#text"
25639 var textNode = node.firstChild;
25640 node.removeChild(textNode);
25641 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25642 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25644 node.parentNode.insertBefore(textNode, node);
25645 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25646 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25648 node.parentNode.removeChild(node);
25651 if (node.nodeName == "#text") {
25652 // clean up silly Windows -- stuff?
25655 if (node.nodeName == "#comment") {
25656 node.parentNode.removeChild(node);
25657 // clean up silly Windows -- stuff?
25661 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25662 node.parentNode.removeChild(node);
25665 //Roo.log(node.tagName);
25666 // remove - but keep children..
25667 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25668 //Roo.log('-- removed');
25669 while (node.childNodes.length) {
25670 var cn = node.childNodes[0];
25671 node.removeChild(cn);
25672 node.parentNode.insertBefore(cn, node);
25673 // move node to parent - and clean it..
25674 this.cleanWord(cn);
25676 node.parentNode.removeChild(node);
25677 /// no need to iterate chidlren = it's got none..
25678 //this.iterateChildren(node, this.cleanWord);
25682 if (node.className.length) {
25684 var cn = node.className.split(/\W+/);
25686 Roo.each(cn, function(cls) {
25687 if (cls.match(/Mso[a-zA-Z]+/)) {
25692 node.className = cna.length ? cna.join(' ') : '';
25694 node.removeAttribute("class");
25698 if (node.hasAttribute("lang")) {
25699 node.removeAttribute("lang");
25702 if (node.hasAttribute("style")) {
25704 var styles = node.getAttribute("style").split(";");
25706 Roo.each(styles, function(s) {
25707 if (!s.match(/:/)) {
25710 var kv = s.split(":");
25711 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25714 // what ever is left... we allow.
25717 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25718 if (!nstyle.length) {
25719 node.removeAttribute('style');
25722 this.iterateChildren(node, this.cleanWord);
25728 * iterateChildren of a Node, calling fn each time, using this as the scole..
25729 * @param {DomNode} node node to iterate children of.
25730 * @param {Function} fn method of this class to call on each item.
25732 iterateChildren : function(node, fn)
25734 if (!node.childNodes.length) {
25737 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25738 fn.call(this, node.childNodes[i])
25744 * cleanTableWidths.
25746 * Quite often pasting from word etc.. results in tables with column and widths.
25747 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25750 cleanTableWidths : function(node)
25755 this.cleanTableWidths(this.doc.body);
25760 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25763 Roo.log(node.tagName);
25764 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25765 this.iterateChildren(node, this.cleanTableWidths);
25768 if (node.hasAttribute('width')) {
25769 node.removeAttribute('width');
25773 if (node.hasAttribute("style")) {
25776 var styles = node.getAttribute("style").split(";");
25778 Roo.each(styles, function(s) {
25779 if (!s.match(/:/)) {
25782 var kv = s.split(":");
25783 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25786 // what ever is left... we allow.
25789 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25790 if (!nstyle.length) {
25791 node.removeAttribute('style');
25795 this.iterateChildren(node, this.cleanTableWidths);
25803 domToHTML : function(currentElement, depth, nopadtext) {
25805 depth = depth || 0;
25806 nopadtext = nopadtext || false;
25808 if (!currentElement) {
25809 return this.domToHTML(this.doc.body);
25812 //Roo.log(currentElement);
25814 var allText = false;
25815 var nodeName = currentElement.nodeName;
25816 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25818 if (nodeName == '#text') {
25820 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25825 if (nodeName != 'BODY') {
25828 // Prints the node tagName, such as <A>, <IMG>, etc
25831 for(i = 0; i < currentElement.attributes.length;i++) {
25833 var aname = currentElement.attributes.item(i).name;
25834 if (!currentElement.attributes.item(i).value.length) {
25837 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25840 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25849 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25852 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25857 // Traverse the tree
25859 var currentElementChild = currentElement.childNodes.item(i);
25860 var allText = true;
25861 var innerHTML = '';
25863 while (currentElementChild) {
25864 // Formatting code (indent the tree so it looks nice on the screen)
25865 var nopad = nopadtext;
25866 if (lastnode == 'SPAN') {
25870 if (currentElementChild.nodeName == '#text') {
25871 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25872 toadd = nopadtext ? toadd : toadd.trim();
25873 if (!nopad && toadd.length > 80) {
25874 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25876 innerHTML += toadd;
25879 currentElementChild = currentElement.childNodes.item(i);
25885 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25887 // Recursively traverse the tree structure of the child node
25888 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25889 lastnode = currentElementChild.nodeName;
25891 currentElementChild=currentElement.childNodes.item(i);
25897 // The remaining code is mostly for formatting the tree
25898 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25903 ret+= "</"+tagName+">";
25909 applyBlacklists : function()
25911 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25912 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25916 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25917 if (b.indexOf(tag) > -1) {
25920 this.white.push(tag);
25924 Roo.each(w, function(tag) {
25925 if (b.indexOf(tag) > -1) {
25928 if (this.white.indexOf(tag) > -1) {
25931 this.white.push(tag);
25936 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25937 if (w.indexOf(tag) > -1) {
25940 this.black.push(tag);
25944 Roo.each(b, function(tag) {
25945 if (w.indexOf(tag) > -1) {
25948 if (this.black.indexOf(tag) > -1) {
25951 this.black.push(tag);
25956 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25957 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25961 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25962 if (b.indexOf(tag) > -1) {
25965 this.cwhite.push(tag);
25969 Roo.each(w, function(tag) {
25970 if (b.indexOf(tag) > -1) {
25973 if (this.cwhite.indexOf(tag) > -1) {
25976 this.cwhite.push(tag);
25981 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25982 if (w.indexOf(tag) > -1) {
25985 this.cblack.push(tag);
25989 Roo.each(b, function(tag) {
25990 if (w.indexOf(tag) > -1) {
25993 if (this.cblack.indexOf(tag) > -1) {
25996 this.cblack.push(tag);
26001 setStylesheets : function(stylesheets)
26003 if(typeof(stylesheets) == 'string'){
26004 Roo.get(this.iframe.contentDocument.head).createChild({
26006 rel : 'stylesheet',
26015 Roo.each(stylesheets, function(s) {
26020 Roo.get(_this.iframe.contentDocument.head).createChild({
26022 rel : 'stylesheet',
26031 removeStylesheets : function()
26035 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26040 setStyle : function(style)
26042 Roo.get(this.iframe.contentDocument.head).createChild({
26051 // hide stuff that is not compatible
26065 * @event specialkey
26069 * @cfg {String} fieldClass @hide
26072 * @cfg {String} focusClass @hide
26075 * @cfg {String} autoCreate @hide
26078 * @cfg {String} inputType @hide
26081 * @cfg {String} invalidClass @hide
26084 * @cfg {String} invalidText @hide
26087 * @cfg {String} msgFx @hide
26090 * @cfg {String} validateOnBlur @hide
26094 Roo.HtmlEditorCore.white = [
26095 'area', 'br', 'img', 'input', 'hr', 'wbr',
26097 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26098 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26099 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26100 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26101 'table', 'ul', 'xmp',
26103 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26106 'dir', 'menu', 'ol', 'ul', 'dl',
26112 Roo.HtmlEditorCore.black = [
26113 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26115 'base', 'basefont', 'bgsound', 'blink', 'body',
26116 'frame', 'frameset', 'head', 'html', 'ilayer',
26117 'iframe', 'layer', 'link', 'meta', 'object',
26118 'script', 'style' ,'title', 'xml' // clean later..
26120 Roo.HtmlEditorCore.clean = [
26121 'script', 'style', 'title', 'xml'
26123 Roo.HtmlEditorCore.remove = [
26128 Roo.HtmlEditorCore.ablack = [
26132 Roo.HtmlEditorCore.aclean = [
26133 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26137 Roo.HtmlEditorCore.pwhite= [
26138 'http', 'https', 'mailto'
26141 // white listed style attributes.
26142 Roo.HtmlEditorCore.cwhite= [
26143 // 'text-align', /// default is to allow most things..
26149 // black listed style attributes.
26150 Roo.HtmlEditorCore.cblack= [
26151 // 'font-size' -- this can be set by the project
26155 Roo.HtmlEditorCore.swapCodes =[
26156 [ 8211, "–" ],
26157 [ 8212, "—" ],
26174 * @class Roo.bootstrap.HtmlEditor
26175 * @extends Roo.bootstrap.TextArea
26176 * Bootstrap HtmlEditor class
26179 * Create a new HtmlEditor
26180 * @param {Object} config The config object
26183 Roo.bootstrap.HtmlEditor = function(config){
26184 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26185 if (!this.toolbars) {
26186 this.toolbars = [];
26189 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26192 * @event initialize
26193 * Fires when the editor is fully initialized (including the iframe)
26194 * @param {HtmlEditor} this
26199 * Fires when the editor is first receives the focus. Any insertion must wait
26200 * until after this event.
26201 * @param {HtmlEditor} this
26205 * @event beforesync
26206 * Fires before the textarea is updated with content from the editor iframe. Return false
26207 * to cancel the sync.
26208 * @param {HtmlEditor} this
26209 * @param {String} html
26213 * @event beforepush
26214 * Fires before the iframe editor is updated with content from the textarea. Return false
26215 * to cancel the push.
26216 * @param {HtmlEditor} this
26217 * @param {String} html
26222 * Fires when the textarea is updated with content from the editor iframe.
26223 * @param {HtmlEditor} this
26224 * @param {String} html
26229 * Fires when the iframe editor is updated with content from the textarea.
26230 * @param {HtmlEditor} this
26231 * @param {String} html
26235 * @event editmodechange
26236 * Fires when the editor switches edit modes
26237 * @param {HtmlEditor} this
26238 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26240 editmodechange: true,
26242 * @event editorevent
26243 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26244 * @param {HtmlEditor} this
26248 * @event firstfocus
26249 * Fires when on first focus - needed by toolbars..
26250 * @param {HtmlEditor} this
26255 * Auto save the htmlEditor value as a file into Events
26256 * @param {HtmlEditor} this
26260 * @event savedpreview
26261 * preview the saved version of htmlEditor
26262 * @param {HtmlEditor} this
26269 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26273 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26278 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26283 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26288 * @cfg {Number} height (in pixels)
26292 * @cfg {Number} width (in pixels)
26297 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26300 stylesheets: false,
26305 // private properties
26306 validationEvent : false,
26308 initialized : false,
26311 onFocus : Roo.emptyFn,
26313 hideMode:'offsets',
26315 tbContainer : false,
26319 toolbarContainer :function() {
26320 return this.wrap.select('.x-html-editor-tb',true).first();
26324 * Protected method that will not generally be called directly. It
26325 * is called when the editor creates its toolbar. Override this method if you need to
26326 * add custom toolbar buttons.
26327 * @param {HtmlEditor} editor
26329 createToolbar : function(){
26330 Roo.log('renewing');
26331 Roo.log("create toolbars");
26333 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26334 this.toolbars[0].render(this.toolbarContainer());
26338 // if (!editor.toolbars || !editor.toolbars.length) {
26339 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26342 // for (var i =0 ; i < editor.toolbars.length;i++) {
26343 // editor.toolbars[i] = Roo.factory(
26344 // typeof(editor.toolbars[i]) == 'string' ?
26345 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26346 // Roo.bootstrap.HtmlEditor);
26347 // editor.toolbars[i].init(editor);
26353 onRender : function(ct, position)
26355 // Roo.log("Call onRender: " + this.xtype);
26357 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26359 this.wrap = this.inputEl().wrap({
26360 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26363 this.editorcore.onRender(ct, position);
26365 if (this.resizable) {
26366 this.resizeEl = new Roo.Resizable(this.wrap, {
26370 minHeight : this.height,
26371 height: this.height,
26372 handles : this.resizable,
26375 resize : function(r, w, h) {
26376 _t.onResize(w,h); // -something
26382 this.createToolbar(this);
26385 if(!this.width && this.resizable){
26386 this.setSize(this.wrap.getSize());
26388 if (this.resizeEl) {
26389 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26390 // should trigger onReize..
26396 onResize : function(w, h)
26398 Roo.log('resize: ' +w + ',' + h );
26399 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26403 if(this.inputEl() ){
26404 if(typeof w == 'number'){
26405 var aw = w - this.wrap.getFrameWidth('lr');
26406 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26409 if(typeof h == 'number'){
26410 var tbh = -11; // fixme it needs to tool bar size!
26411 for (var i =0; i < this.toolbars.length;i++) {
26412 // fixme - ask toolbars for heights?
26413 tbh += this.toolbars[i].el.getHeight();
26414 //if (this.toolbars[i].footer) {
26415 // tbh += this.toolbars[i].footer.el.getHeight();
26423 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26424 ah -= 5; // knock a few pixes off for look..
26425 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26429 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26430 this.editorcore.onResize(ew,eh);
26435 * Toggles the editor between standard and source edit mode.
26436 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26438 toggleSourceEdit : function(sourceEditMode)
26440 this.editorcore.toggleSourceEdit(sourceEditMode);
26442 if(this.editorcore.sourceEditMode){
26443 Roo.log('editor - showing textarea');
26446 // Roo.log(this.syncValue());
26448 this.inputEl().removeClass(['hide', 'x-hidden']);
26449 this.inputEl().dom.removeAttribute('tabIndex');
26450 this.inputEl().focus();
26452 Roo.log('editor - hiding textarea');
26454 // Roo.log(this.pushValue());
26457 this.inputEl().addClass(['hide', 'x-hidden']);
26458 this.inputEl().dom.setAttribute('tabIndex', -1);
26459 //this.deferFocus();
26462 if(this.resizable){
26463 this.setSize(this.wrap.getSize());
26466 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26469 // private (for BoxComponent)
26470 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26472 // private (for BoxComponent)
26473 getResizeEl : function(){
26477 // private (for BoxComponent)
26478 getPositionEl : function(){
26483 initEvents : function(){
26484 this.originalValue = this.getValue();
26488 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26491 // markInvalid : Roo.emptyFn,
26493 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26496 // clearInvalid : Roo.emptyFn,
26498 setValue : function(v){
26499 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26500 this.editorcore.pushValue();
26505 deferFocus : function(){
26506 this.focus.defer(10, this);
26510 focus : function(){
26511 this.editorcore.focus();
26517 onDestroy : function(){
26523 for (var i =0; i < this.toolbars.length;i++) {
26524 // fixme - ask toolbars for heights?
26525 this.toolbars[i].onDestroy();
26528 this.wrap.dom.innerHTML = '';
26529 this.wrap.remove();
26534 onFirstFocus : function(){
26535 //Roo.log("onFirstFocus");
26536 this.editorcore.onFirstFocus();
26537 for (var i =0; i < this.toolbars.length;i++) {
26538 this.toolbars[i].onFirstFocus();
26544 syncValue : function()
26546 this.editorcore.syncValue();
26549 pushValue : function()
26551 this.editorcore.pushValue();
26555 // hide stuff that is not compatible
26569 * @event specialkey
26573 * @cfg {String} fieldClass @hide
26576 * @cfg {String} focusClass @hide
26579 * @cfg {String} autoCreate @hide
26582 * @cfg {String} inputType @hide
26586 * @cfg {String} invalidText @hide
26589 * @cfg {String} msgFx @hide
26592 * @cfg {String} validateOnBlur @hide
26601 Roo.namespace('Roo.bootstrap.htmleditor');
26603 * @class Roo.bootstrap.HtmlEditorToolbar1
26609 new Roo.bootstrap.HtmlEditor({
26612 new Roo.bootstrap.HtmlEditorToolbar1({
26613 disable : { fonts: 1 , format: 1, ..., ... , ...],
26619 * @cfg {Object} disable List of elements to disable..
26620 * @cfg {Array} btns List of additional buttons.
26624 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26627 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26630 Roo.apply(this, config);
26632 // default disabled, based on 'good practice'..
26633 this.disable = this.disable || {};
26634 Roo.applyIf(this.disable, {
26637 specialElements : true
26639 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26641 this.editor = config.editor;
26642 this.editorcore = config.editor.editorcore;
26644 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26646 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26647 // dont call parent... till later.
26649 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26654 editorcore : false,
26659 "h1","h2","h3","h4","h5","h6",
26661 "abbr", "acronym", "address", "cite", "samp", "var",
26665 onRender : function(ct, position)
26667 // Roo.log("Call onRender: " + this.xtype);
26669 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26671 this.el.dom.style.marginBottom = '0';
26673 var editorcore = this.editorcore;
26674 var editor= this.editor;
26677 var btn = function(id,cmd , toggle, handler, html){
26679 var event = toggle ? 'toggle' : 'click';
26684 xns: Roo.bootstrap,
26688 enableToggle:toggle !== false,
26690 pressed : toggle ? false : null,
26693 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26694 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26700 // var cb_box = function...
26705 xns: Roo.bootstrap,
26710 xns: Roo.bootstrap,
26714 Roo.each(this.formats, function(f) {
26715 style.menu.items.push({
26717 xns: Roo.bootstrap,
26718 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26723 editorcore.insertTag(this.tagname);
26730 children.push(style);
26732 btn('bold',false,true);
26733 btn('italic',false,true);
26734 btn('align-left', 'justifyleft',true);
26735 btn('align-center', 'justifycenter',true);
26736 btn('align-right' , 'justifyright',true);
26737 btn('link', false, false, function(btn) {
26738 //Roo.log("create link?");
26739 var url = prompt(this.createLinkText, this.defaultLinkValue);
26740 if(url && url != 'http:/'+'/'){
26741 this.editorcore.relayCmd('createlink', url);
26744 btn('list','insertunorderedlist',true);
26745 btn('pencil', false,true, function(btn){
26747 this.toggleSourceEdit(btn.pressed);
26750 if (this.editor.btns.length > 0) {
26751 for (var i = 0; i<this.editor.btns.length; i++) {
26752 children.push(this.editor.btns[i]);
26760 xns: Roo.bootstrap,
26765 xns: Roo.bootstrap,
26770 cog.menu.items.push({
26772 xns: Roo.bootstrap,
26773 html : Clean styles,
26778 editorcore.insertTag(this.tagname);
26787 this.xtype = 'NavSimplebar';
26789 for(var i=0;i< children.length;i++) {
26791 this.buttons.add(this.addxtypeChild(children[i]));
26795 editor.on('editorevent', this.updateToolbar, this);
26797 onBtnClick : function(id)
26799 this.editorcore.relayCmd(id);
26800 this.editorcore.focus();
26804 * Protected method that will not generally be called directly. It triggers
26805 * a toolbar update by reading the markup state of the current selection in the editor.
26807 updateToolbar: function(){
26809 if(!this.editorcore.activated){
26810 this.editor.onFirstFocus(); // is this neeed?
26814 var btns = this.buttons;
26815 var doc = this.editorcore.doc;
26816 btns.get('bold').setActive(doc.queryCommandState('bold'));
26817 btns.get('italic').setActive(doc.queryCommandState('italic'));
26818 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26820 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26821 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26822 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26824 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26825 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26828 var ans = this.editorcore.getAllAncestors();
26829 if (this.formatCombo) {
26832 var store = this.formatCombo.store;
26833 this.formatCombo.setValue("");
26834 for (var i =0; i < ans.length;i++) {
26835 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26837 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26845 // hides menus... - so this cant be on a menu...
26846 Roo.bootstrap.MenuMgr.hideAll();
26848 Roo.bootstrap.MenuMgr.hideAll();
26849 //this.editorsyncValue();
26851 onFirstFocus: function() {
26852 this.buttons.each(function(item){
26856 toggleSourceEdit : function(sourceEditMode){
26859 if(sourceEditMode){
26860 Roo.log("disabling buttons");
26861 this.buttons.each( function(item){
26862 if(item.cmd != 'pencil'){
26868 Roo.log("enabling buttons");
26869 if(this.editorcore.initialized){
26870 this.buttons.each( function(item){
26876 Roo.log("calling toggole on editor");
26877 // tell the editor that it's been pressed..
26878 this.editor.toggleSourceEdit(sourceEditMode);
26892 * @class Roo.bootstrap.Markdown
26893 * @extends Roo.bootstrap.TextArea
26894 * Bootstrap Showdown editable area
26895 * @cfg {string} content
26898 * Create a new Showdown
26901 Roo.bootstrap.Markdown = function(config){
26902 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26906 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26910 initEvents : function()
26913 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26914 this.markdownEl = this.el.createChild({
26915 cls : 'roo-markdown-area'
26917 this.inputEl().addClass('d-none');
26918 if (this.getValue() == '') {
26919 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26922 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26924 this.markdownEl.on('click', this.toggleTextEdit, this);
26925 this.on('blur', this.toggleTextEdit, this);
26926 this.on('specialkey', this.resizeTextArea, this);
26929 toggleTextEdit : function()
26931 var sh = this.markdownEl.getHeight();
26932 this.inputEl().addClass('d-none');
26933 this.markdownEl.addClass('d-none');
26934 if (!this.editing) {
26936 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26937 this.inputEl().removeClass('d-none');
26938 this.inputEl().focus();
26939 this.editing = true;
26942 // show showdown...
26943 this.updateMarkdown();
26944 this.markdownEl.removeClass('d-none');
26945 this.editing = false;
26948 updateMarkdown : function()
26950 if (this.getValue() == '') {
26951 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26955 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26958 resizeTextArea: function () {
26961 Roo.log([sh, this.getValue().split("\n").length * 30]);
26962 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26964 setValue : function(val)
26966 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26967 if (!this.editing) {
26968 this.updateMarkdown();
26974 if (!this.editing) {
26975 this.toggleTextEdit();
26983 * @class Roo.bootstrap.Table.AbstractSelectionModel
26984 * @extends Roo.util.Observable
26985 * Abstract base class for grid SelectionModels. It provides the interface that should be
26986 * implemented by descendant classes. This class should not be directly instantiated.
26989 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26990 this.locked = false;
26991 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26995 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26996 /** @ignore Called by the grid automatically. Do not call directly. */
26997 init : function(grid){
27003 * Locks the selections.
27006 this.locked = true;
27010 * Unlocks the selections.
27012 unlock : function(){
27013 this.locked = false;
27017 * Returns true if the selections are locked.
27018 * @return {Boolean}
27020 isLocked : function(){
27021 return this.locked;
27025 initEvents : function ()
27031 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27032 * @class Roo.bootstrap.Table.RowSelectionModel
27033 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27034 * It supports multiple selections and keyboard selection/navigation.
27036 * @param {Object} config
27039 Roo.bootstrap.Table.RowSelectionModel = function(config){
27040 Roo.apply(this, config);
27041 this.selections = new Roo.util.MixedCollection(false, function(o){
27046 this.lastActive = false;
27050 * @event selectionchange
27051 * Fires when the selection changes
27052 * @param {SelectionModel} this
27054 "selectionchange" : true,
27056 * @event afterselectionchange
27057 * Fires after the selection changes (eg. by key press or clicking)
27058 * @param {SelectionModel} this
27060 "afterselectionchange" : true,
27062 * @event beforerowselect
27063 * Fires when a row is selected being selected, return false to cancel.
27064 * @param {SelectionModel} this
27065 * @param {Number} rowIndex The selected index
27066 * @param {Boolean} keepExisting False if other selections will be cleared
27068 "beforerowselect" : true,
27071 * Fires when a row is selected.
27072 * @param {SelectionModel} this
27073 * @param {Number} rowIndex The selected index
27074 * @param {Roo.data.Record} r The record
27076 "rowselect" : true,
27078 * @event rowdeselect
27079 * Fires when a row is deselected.
27080 * @param {SelectionModel} this
27081 * @param {Number} rowIndex The selected index
27083 "rowdeselect" : true
27085 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27086 this.locked = false;
27089 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27091 * @cfg {Boolean} singleSelect
27092 * True to allow selection of only one row at a time (defaults to false)
27094 singleSelect : false,
27097 initEvents : function()
27100 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27101 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27102 //}else{ // allow click to work like normal
27103 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27105 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27106 this.grid.on("rowclick", this.handleMouseDown, this);
27108 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27109 "up" : function(e){
27111 this.selectPrevious(e.shiftKey);
27112 }else if(this.last !== false && this.lastActive !== false){
27113 var last = this.last;
27114 this.selectRange(this.last, this.lastActive-1);
27115 this.grid.getView().focusRow(this.lastActive);
27116 if(last !== false){
27120 this.selectFirstRow();
27122 this.fireEvent("afterselectionchange", this);
27124 "down" : function(e){
27126 this.selectNext(e.shiftKey);
27127 }else if(this.last !== false && this.lastActive !== false){
27128 var last = this.last;
27129 this.selectRange(this.last, this.lastActive+1);
27130 this.grid.getView().focusRow(this.lastActive);
27131 if(last !== false){
27135 this.selectFirstRow();
27137 this.fireEvent("afterselectionchange", this);
27141 this.grid.store.on('load', function(){
27142 this.selections.clear();
27145 var view = this.grid.view;
27146 view.on("refresh", this.onRefresh, this);
27147 view.on("rowupdated", this.onRowUpdated, this);
27148 view.on("rowremoved", this.onRemove, this);
27153 onRefresh : function()
27155 var ds = this.grid.store, i, v = this.grid.view;
27156 var s = this.selections;
27157 s.each(function(r){
27158 if((i = ds.indexOfId(r.id)) != -1){
27167 onRemove : function(v, index, r){
27168 this.selections.remove(r);
27172 onRowUpdated : function(v, index, r){
27173 if(this.isSelected(r)){
27174 v.onRowSelect(index);
27180 * @param {Array} records The records to select
27181 * @param {Boolean} keepExisting (optional) True to keep existing selections
27183 selectRecords : function(records, keepExisting)
27186 this.clearSelections();
27188 var ds = this.grid.store;
27189 for(var i = 0, len = records.length; i < len; i++){
27190 this.selectRow(ds.indexOf(records[i]), true);
27195 * Gets the number of selected rows.
27198 getCount : function(){
27199 return this.selections.length;
27203 * Selects the first row in the grid.
27205 selectFirstRow : function(){
27210 * Select the last row.
27211 * @param {Boolean} keepExisting (optional) True to keep existing selections
27213 selectLastRow : function(keepExisting){
27214 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27215 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27219 * Selects the row immediately following the last selected row.
27220 * @param {Boolean} keepExisting (optional) True to keep existing selections
27222 selectNext : function(keepExisting)
27224 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27225 this.selectRow(this.last+1, keepExisting);
27226 this.grid.getView().focusRow(this.last);
27231 * Selects the row that precedes the last selected row.
27232 * @param {Boolean} keepExisting (optional) True to keep existing selections
27234 selectPrevious : function(keepExisting){
27236 this.selectRow(this.last-1, keepExisting);
27237 this.grid.getView().focusRow(this.last);
27242 * Returns the selected records
27243 * @return {Array} Array of selected records
27245 getSelections : function(){
27246 return [].concat(this.selections.items);
27250 * Returns the first selected record.
27253 getSelected : function(){
27254 return this.selections.itemAt(0);
27259 * Clears all selections.
27261 clearSelections : function(fast)
27267 var ds = this.grid.store;
27268 var s = this.selections;
27269 s.each(function(r){
27270 this.deselectRow(ds.indexOfId(r.id));
27274 this.selections.clear();
27281 * Selects all rows.
27283 selectAll : function(){
27287 this.selections.clear();
27288 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27289 this.selectRow(i, true);
27294 * Returns True if there is a selection.
27295 * @return {Boolean}
27297 hasSelection : function(){
27298 return this.selections.length > 0;
27302 * Returns True if the specified row is selected.
27303 * @param {Number/Record} record The record or index of the record to check
27304 * @return {Boolean}
27306 isSelected : function(index){
27307 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27308 return (r && this.selections.key(r.id) ? true : false);
27312 * Returns True if the specified record id is selected.
27313 * @param {String} id The id of record to check
27314 * @return {Boolean}
27316 isIdSelected : function(id){
27317 return (this.selections.key(id) ? true : false);
27322 handleMouseDBClick : function(e, t){
27326 handleMouseDown : function(e, t)
27328 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27329 if(this.isLocked() || rowIndex < 0 ){
27332 if(e.shiftKey && this.last !== false){
27333 var last = this.last;
27334 this.selectRange(last, rowIndex, e.ctrlKey);
27335 this.last = last; // reset the last
27339 var isSelected = this.isSelected(rowIndex);
27340 //Roo.log("select row:" + rowIndex);
27342 this.deselectRow(rowIndex);
27344 this.selectRow(rowIndex, true);
27348 if(e.button !== 0 && isSelected){
27349 alert('rowIndex 2: ' + rowIndex);
27350 view.focusRow(rowIndex);
27351 }else if(e.ctrlKey && isSelected){
27352 this.deselectRow(rowIndex);
27353 }else if(!isSelected){
27354 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27355 view.focusRow(rowIndex);
27359 this.fireEvent("afterselectionchange", this);
27362 handleDragableRowClick : function(grid, rowIndex, e)
27364 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27365 this.selectRow(rowIndex, false);
27366 grid.view.focusRow(rowIndex);
27367 this.fireEvent("afterselectionchange", this);
27372 * Selects multiple rows.
27373 * @param {Array} rows Array of the indexes of the row to select
27374 * @param {Boolean} keepExisting (optional) True to keep existing selections
27376 selectRows : function(rows, keepExisting){
27378 this.clearSelections();
27380 for(var i = 0, len = rows.length; i < len; i++){
27381 this.selectRow(rows[i], true);
27386 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27387 * @param {Number} startRow The index of the first row in the range
27388 * @param {Number} endRow The index of the last row in the range
27389 * @param {Boolean} keepExisting (optional) True to retain existing selections
27391 selectRange : function(startRow, endRow, keepExisting){
27396 this.clearSelections();
27398 if(startRow <= endRow){
27399 for(var i = startRow; i <= endRow; i++){
27400 this.selectRow(i, true);
27403 for(var i = startRow; i >= endRow; i--){
27404 this.selectRow(i, true);
27410 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27411 * @param {Number} startRow The index of the first row in the range
27412 * @param {Number} endRow The index of the last row in the range
27414 deselectRange : function(startRow, endRow, preventViewNotify){
27418 for(var i = startRow; i <= endRow; i++){
27419 this.deselectRow(i, preventViewNotify);
27425 * @param {Number} row The index of the row to select
27426 * @param {Boolean} keepExisting (optional) True to keep existing selections
27428 selectRow : function(index, keepExisting, preventViewNotify)
27430 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27433 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27434 if(!keepExisting || this.singleSelect){
27435 this.clearSelections();
27438 var r = this.grid.store.getAt(index);
27439 //console.log('selectRow - record id :' + r.id);
27441 this.selections.add(r);
27442 this.last = this.lastActive = index;
27443 if(!preventViewNotify){
27444 var proxy = new Roo.Element(
27445 this.grid.getRowDom(index)
27447 proxy.addClass('bg-info info');
27449 this.fireEvent("rowselect", this, index, r);
27450 this.fireEvent("selectionchange", this);
27456 * @param {Number} row The index of the row to deselect
27458 deselectRow : function(index, preventViewNotify)
27463 if(this.last == index){
27466 if(this.lastActive == index){
27467 this.lastActive = false;
27470 var r = this.grid.store.getAt(index);
27475 this.selections.remove(r);
27476 //.console.log('deselectRow - record id :' + r.id);
27477 if(!preventViewNotify){
27479 var proxy = new Roo.Element(
27480 this.grid.getRowDom(index)
27482 proxy.removeClass('bg-info info');
27484 this.fireEvent("rowdeselect", this, index);
27485 this.fireEvent("selectionchange", this);
27489 restoreLast : function(){
27491 this.last = this._last;
27496 acceptsNav : function(row, col, cm){
27497 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27501 onEditorKey : function(field, e){
27502 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27507 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27509 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27511 }else if(k == e.ENTER && !e.ctrlKey){
27515 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27517 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27519 }else if(k == e.ESC){
27523 g.startEditing(newCell[0], newCell[1]);
27529 * Ext JS Library 1.1.1
27530 * Copyright(c) 2006-2007, Ext JS, LLC.
27532 * Originally Released Under LGPL - original licence link has changed is not relivant.
27535 * <script type="text/javascript">
27539 * @class Roo.bootstrap.PagingToolbar
27540 * @extends Roo.bootstrap.NavSimplebar
27541 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27543 * Create a new PagingToolbar
27544 * @param {Object} config The config object
27545 * @param {Roo.data.Store} store
27547 Roo.bootstrap.PagingToolbar = function(config)
27549 // old args format still supported... - xtype is prefered..
27550 // created from xtype...
27552 this.ds = config.dataSource;
27554 if (config.store && !this.ds) {
27555 this.store= Roo.factory(config.store, Roo.data);
27556 this.ds = this.store;
27557 this.ds.xmodule = this.xmodule || false;
27560 this.toolbarItems = [];
27561 if (config.items) {
27562 this.toolbarItems = config.items;
27565 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27570 this.bind(this.ds);
27573 if (Roo.bootstrap.version == 4) {
27574 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27576 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27581 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27583 * @cfg {Roo.data.Store} dataSource
27584 * The underlying data store providing the paged data
27587 * @cfg {String/HTMLElement/Element} container
27588 * container The id or element that will contain the toolbar
27591 * @cfg {Boolean} displayInfo
27592 * True to display the displayMsg (defaults to false)
27595 * @cfg {Number} pageSize
27596 * The number of records to display per page (defaults to 20)
27600 * @cfg {String} displayMsg
27601 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27603 displayMsg : 'Displaying {0} - {1} of {2}',
27605 * @cfg {String} emptyMsg
27606 * The message to display when no records are found (defaults to "No data to display")
27608 emptyMsg : 'No data to display',
27610 * Customizable piece of the default paging text (defaults to "Page")
27613 beforePageText : "Page",
27615 * Customizable piece of the default paging text (defaults to "of %0")
27618 afterPageText : "of {0}",
27620 * Customizable piece of the default paging text (defaults to "First Page")
27623 firstText : "First Page",
27625 * Customizable piece of the default paging text (defaults to "Previous Page")
27628 prevText : "Previous Page",
27630 * Customizable piece of the default paging text (defaults to "Next Page")
27633 nextText : "Next Page",
27635 * Customizable piece of the default paging text (defaults to "Last Page")
27638 lastText : "Last Page",
27640 * Customizable piece of the default paging text (defaults to "Refresh")
27643 refreshText : "Refresh",
27647 onRender : function(ct, position)
27649 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27650 this.navgroup.parentId = this.id;
27651 this.navgroup.onRender(this.el, null);
27652 // add the buttons to the navgroup
27654 if(this.displayInfo){
27655 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27656 this.displayEl = this.el.select('.x-paging-info', true).first();
27657 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27658 // this.displayEl = navel.el.select('span',true).first();
27664 Roo.each(_this.buttons, function(e){ // this might need to use render????
27665 Roo.factory(e).render(_this.el);
27669 Roo.each(_this.toolbarItems, function(e) {
27670 _this.navgroup.addItem(e);
27674 this.first = this.navgroup.addItem({
27675 tooltip: this.firstText,
27676 cls: "prev btn-outline-secondary",
27677 html : ' <i class="fa fa-step-backward"></i>',
27679 preventDefault: true,
27680 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27683 this.prev = this.navgroup.addItem({
27684 tooltip: this.prevText,
27685 cls: "prev btn-outline-secondary",
27686 html : ' <i class="fa fa-backward"></i>',
27688 preventDefault: true,
27689 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27691 //this.addSeparator();
27694 var field = this.navgroup.addItem( {
27696 cls : 'x-paging-position btn-outline-secondary',
27698 html : this.beforePageText +
27699 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27700 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27703 this.field = field.el.select('input', true).first();
27704 this.field.on("keydown", this.onPagingKeydown, this);
27705 this.field.on("focus", function(){this.dom.select();});
27708 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27709 //this.field.setHeight(18);
27710 //this.addSeparator();
27711 this.next = this.navgroup.addItem({
27712 tooltip: this.nextText,
27713 cls: "next btn-outline-secondary",
27714 html : ' <i class="fa fa-forward"></i>',
27716 preventDefault: true,
27717 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27719 this.last = this.navgroup.addItem({
27720 tooltip: this.lastText,
27721 html : ' <i class="fa fa-step-forward"></i>',
27722 cls: "next btn-outline-secondary",
27724 preventDefault: true,
27725 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27727 //this.addSeparator();
27728 this.loading = this.navgroup.addItem({
27729 tooltip: this.refreshText,
27730 cls: "btn-outline-secondary",
27731 html : ' <i class="fa fa-refresh"></i>',
27732 preventDefault: true,
27733 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27739 updateInfo : function(){
27740 if(this.displayEl){
27741 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27742 var msg = count == 0 ?
27746 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27748 this.displayEl.update(msg);
27753 onLoad : function(ds, r, o)
27755 this.cursor = o.params && o.params.start ? o.params.start : 0;
27757 var d = this.getPageData(),
27762 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27763 this.field.dom.value = ap;
27764 this.first.setDisabled(ap == 1);
27765 this.prev.setDisabled(ap == 1);
27766 this.next.setDisabled(ap == ps);
27767 this.last.setDisabled(ap == ps);
27768 this.loading.enable();
27773 getPageData : function(){
27774 var total = this.ds.getTotalCount();
27777 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27778 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27783 onLoadError : function(){
27784 this.loading.enable();
27788 onPagingKeydown : function(e){
27789 var k = e.getKey();
27790 var d = this.getPageData();
27792 var v = this.field.dom.value, pageNum;
27793 if(!v || isNaN(pageNum = parseInt(v, 10))){
27794 this.field.dom.value = d.activePage;
27797 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27798 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27801 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))
27803 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27804 this.field.dom.value = pageNum;
27805 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27808 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27810 var v = this.field.dom.value, pageNum;
27811 var increment = (e.shiftKey) ? 10 : 1;
27812 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27815 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27816 this.field.dom.value = d.activePage;
27819 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27821 this.field.dom.value = parseInt(v, 10) + increment;
27822 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27823 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27830 beforeLoad : function(){
27832 this.loading.disable();
27837 onClick : function(which){
27846 ds.load({params:{start: 0, limit: this.pageSize}});
27849 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27852 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27855 var total = ds.getTotalCount();
27856 var extra = total % this.pageSize;
27857 var lastStart = extra ? (total - extra) : total-this.pageSize;
27858 ds.load({params:{start: lastStart, limit: this.pageSize}});
27861 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27867 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27868 * @param {Roo.data.Store} store The data store to unbind
27870 unbind : function(ds){
27871 ds.un("beforeload", this.beforeLoad, this);
27872 ds.un("load", this.onLoad, this);
27873 ds.un("loadexception", this.onLoadError, this);
27874 ds.un("remove", this.updateInfo, this);
27875 ds.un("add", this.updateInfo, this);
27876 this.ds = undefined;
27880 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27881 * @param {Roo.data.Store} store The data store to bind
27883 bind : function(ds){
27884 ds.on("beforeload", this.beforeLoad, this);
27885 ds.on("load", this.onLoad, this);
27886 ds.on("loadexception", this.onLoadError, this);
27887 ds.on("remove", this.updateInfo, this);
27888 ds.on("add", this.updateInfo, this);
27899 * @class Roo.bootstrap.MessageBar
27900 * @extends Roo.bootstrap.Component
27901 * Bootstrap MessageBar class
27902 * @cfg {String} html contents of the MessageBar
27903 * @cfg {String} weight (info | success | warning | danger) default info
27904 * @cfg {String} beforeClass insert the bar before the given class
27905 * @cfg {Boolean} closable (true | false) default false
27906 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27909 * Create a new Element
27910 * @param {Object} config The config object
27913 Roo.bootstrap.MessageBar = function(config){
27914 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27917 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27923 beforeClass: 'bootstrap-sticky-wrap',
27925 getAutoCreate : function(){
27929 cls: 'alert alert-dismissable alert-' + this.weight,
27934 html: this.html || ''
27940 cfg.cls += ' alert-messages-fixed';
27954 onRender : function(ct, position)
27956 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27959 var cfg = Roo.apply({}, this.getAutoCreate());
27963 cfg.cls += ' ' + this.cls;
27966 cfg.style = this.style;
27968 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27970 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27973 this.el.select('>button.close').on('click', this.hide, this);
27979 if (!this.rendered) {
27985 this.fireEvent('show', this);
27991 if (!this.rendered) {
27997 this.fireEvent('hide', this);
28000 update : function()
28002 // var e = this.el.dom.firstChild;
28004 // if(this.closable){
28005 // e = e.nextSibling;
28008 // e.data = this.html || '';
28010 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28026 * @class Roo.bootstrap.Graph
28027 * @extends Roo.bootstrap.Component
28028 * Bootstrap Graph class
28032 @cfg {String} graphtype bar | vbar | pie
28033 @cfg {number} g_x coodinator | centre x (pie)
28034 @cfg {number} g_y coodinator | centre y (pie)
28035 @cfg {number} g_r radius (pie)
28036 @cfg {number} g_height height of the chart (respected by all elements in the set)
28037 @cfg {number} g_width width of the chart (respected by all elements in the set)
28038 @cfg {Object} title The title of the chart
28041 -opts (object) options for the chart
28043 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28044 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28046 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.
28047 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28049 o stretch (boolean)
28051 -opts (object) options for the pie
28054 o startAngle (number)
28055 o endAngle (number)
28059 * Create a new Input
28060 * @param {Object} config The config object
28063 Roo.bootstrap.Graph = function(config){
28064 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28070 * The img click event for the img.
28071 * @param {Roo.EventObject} e
28077 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28088 //g_colors: this.colors,
28095 getAutoCreate : function(){
28106 onRender : function(ct,position){
28109 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28111 if (typeof(Raphael) == 'undefined') {
28112 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28116 this.raphael = Raphael(this.el.dom);
28118 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28119 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28120 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28121 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28123 r.text(160, 10, "Single Series Chart").attr(txtattr);
28124 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28125 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28126 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28128 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28129 r.barchart(330, 10, 300, 220, data1);
28130 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28131 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28134 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28135 // r.barchart(30, 30, 560, 250, xdata, {
28136 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28137 // axis : "0 0 1 1",
28138 // axisxlabels : xdata
28139 // //yvalues : cols,
28142 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28144 // this.load(null,xdata,{
28145 // axis : "0 0 1 1",
28146 // axisxlabels : xdata
28151 load : function(graphtype,xdata,opts)
28153 this.raphael.clear();
28155 graphtype = this.graphtype;
28160 var r = this.raphael,
28161 fin = function () {
28162 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28164 fout = function () {
28165 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28167 pfin = function() {
28168 this.sector.stop();
28169 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28172 this.label[0].stop();
28173 this.label[0].attr({ r: 7.5 });
28174 this.label[1].attr({ "font-weight": 800 });
28177 pfout = function() {
28178 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28181 this.label[0].animate({ r: 5 }, 500, "bounce");
28182 this.label[1].attr({ "font-weight": 400 });
28188 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28191 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28194 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28195 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28197 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28204 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28209 setTitle: function(o)
28214 initEvents: function() {
28217 this.el.on('click', this.onClick, this);
28221 onClick : function(e)
28223 Roo.log('img onclick');
28224 this.fireEvent('click', this, e);
28236 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28239 * @class Roo.bootstrap.dash.NumberBox
28240 * @extends Roo.bootstrap.Component
28241 * Bootstrap NumberBox class
28242 * @cfg {String} headline Box headline
28243 * @cfg {String} content Box content
28244 * @cfg {String} icon Box icon
28245 * @cfg {String} footer Footer text
28246 * @cfg {String} fhref Footer href
28249 * Create a new NumberBox
28250 * @param {Object} config The config object
28254 Roo.bootstrap.dash.NumberBox = function(config){
28255 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28259 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28268 getAutoCreate : function(){
28272 cls : 'small-box ',
28280 cls : 'roo-headline',
28281 html : this.headline
28285 cls : 'roo-content',
28286 html : this.content
28300 cls : 'ion ' + this.icon
28309 cls : 'small-box-footer',
28310 href : this.fhref || '#',
28314 cfg.cn.push(footer);
28321 onRender : function(ct,position){
28322 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28329 setHeadline: function (value)
28331 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28334 setFooter: function (value, href)
28336 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28339 this.el.select('a.small-box-footer',true).first().attr('href', href);
28344 setContent: function (value)
28346 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28349 initEvents: function()
28363 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28366 * @class Roo.bootstrap.dash.TabBox
28367 * @extends Roo.bootstrap.Component
28368 * Bootstrap TabBox class
28369 * @cfg {String} title Title of the TabBox
28370 * @cfg {String} icon Icon of the TabBox
28371 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28372 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28375 * Create a new TabBox
28376 * @param {Object} config The config object
28380 Roo.bootstrap.dash.TabBox = function(config){
28381 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28386 * When a pane is added
28387 * @param {Roo.bootstrap.dash.TabPane} pane
28391 * @event activatepane
28392 * When a pane is activated
28393 * @param {Roo.bootstrap.dash.TabPane} pane
28395 "activatepane" : true
28403 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28408 tabScrollable : false,
28410 getChildContainer : function()
28412 return this.el.select('.tab-content', true).first();
28415 getAutoCreate : function(){
28419 cls: 'pull-left header',
28427 cls: 'fa ' + this.icon
28433 cls: 'nav nav-tabs pull-right',
28439 if(this.tabScrollable){
28446 cls: 'nav nav-tabs pull-right',
28457 cls: 'nav-tabs-custom',
28462 cls: 'tab-content no-padding',
28470 initEvents : function()
28472 //Roo.log('add add pane handler');
28473 this.on('addpane', this.onAddPane, this);
28476 * Updates the box title
28477 * @param {String} html to set the title to.
28479 setTitle : function(value)
28481 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28483 onAddPane : function(pane)
28485 this.panes.push(pane);
28486 //Roo.log('addpane');
28488 // tabs are rendere left to right..
28489 if(!this.showtabs){
28493 var ctr = this.el.select('.nav-tabs', true).first();
28496 var existing = ctr.select('.nav-tab',true);
28497 var qty = existing.getCount();;
28500 var tab = ctr.createChild({
28502 cls : 'nav-tab' + (qty ? '' : ' active'),
28510 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28513 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28515 pane.el.addClass('active');
28520 onTabClick : function(ev,un,ob,pane)
28522 //Roo.log('tab - prev default');
28523 ev.preventDefault();
28526 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28527 pane.tab.addClass('active');
28528 //Roo.log(pane.title);
28529 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28530 // technically we should have a deactivate event.. but maybe add later.
28531 // and it should not de-activate the selected tab...
28532 this.fireEvent('activatepane', pane);
28533 pane.el.addClass('active');
28534 pane.fireEvent('activate');
28539 getActivePane : function()
28542 Roo.each(this.panes, function(p) {
28543 if(p.el.hasClass('active')){
28564 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28566 * @class Roo.bootstrap.TabPane
28567 * @extends Roo.bootstrap.Component
28568 * Bootstrap TabPane class
28569 * @cfg {Boolean} active (false | true) Default false
28570 * @cfg {String} title title of panel
28574 * Create a new TabPane
28575 * @param {Object} config The config object
28578 Roo.bootstrap.dash.TabPane = function(config){
28579 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28585 * When a pane is activated
28586 * @param {Roo.bootstrap.dash.TabPane} pane
28593 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28598 // the tabBox that this is attached to.
28601 getAutoCreate : function()
28609 cfg.cls += ' active';
28614 initEvents : function()
28616 //Roo.log('trigger add pane handler');
28617 this.parent().fireEvent('addpane', this)
28621 * Updates the tab title
28622 * @param {String} html to set the title to.
28624 setTitle: function(str)
28630 this.tab.select('a', true).first().dom.innerHTML = str;
28647 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28650 * @class Roo.bootstrap.menu.Menu
28651 * @extends Roo.bootstrap.Component
28652 * Bootstrap Menu class - container for Menu
28653 * @cfg {String} html Text of the menu
28654 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28655 * @cfg {String} icon Font awesome icon
28656 * @cfg {String} pos Menu align to (top | bottom) default bottom
28660 * Create a new Menu
28661 * @param {Object} config The config object
28665 Roo.bootstrap.menu.Menu = function(config){
28666 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28670 * @event beforeshow
28671 * Fires before this menu is displayed
28672 * @param {Roo.bootstrap.menu.Menu} this
28676 * @event beforehide
28677 * Fires before this menu is hidden
28678 * @param {Roo.bootstrap.menu.Menu} this
28683 * Fires after this menu is displayed
28684 * @param {Roo.bootstrap.menu.Menu} this
28689 * Fires after this menu is hidden
28690 * @param {Roo.bootstrap.menu.Menu} this
28695 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28696 * @param {Roo.bootstrap.menu.Menu} this
28697 * @param {Roo.EventObject} e
28704 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28708 weight : 'default',
28713 getChildContainer : function() {
28714 if(this.isSubMenu){
28718 return this.el.select('ul.dropdown-menu', true).first();
28721 getAutoCreate : function()
28726 cls : 'roo-menu-text',
28734 cls : 'fa ' + this.icon
28745 cls : 'dropdown-button btn btn-' + this.weight,
28750 cls : 'dropdown-toggle btn btn-' + this.weight,
28760 cls : 'dropdown-menu'
28766 if(this.pos == 'top'){
28767 cfg.cls += ' dropup';
28770 if(this.isSubMenu){
28773 cls : 'dropdown-menu'
28780 onRender : function(ct, position)
28782 this.isSubMenu = ct.hasClass('dropdown-submenu');
28784 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28787 initEvents : function()
28789 if(this.isSubMenu){
28793 this.hidden = true;
28795 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28796 this.triggerEl.on('click', this.onTriggerPress, this);
28798 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28799 this.buttonEl.on('click', this.onClick, this);
28805 if(this.isSubMenu){
28809 return this.el.select('ul.dropdown-menu', true).first();
28812 onClick : function(e)
28814 this.fireEvent("click", this, e);
28817 onTriggerPress : function(e)
28819 if (this.isVisible()) {
28826 isVisible : function(){
28827 return !this.hidden;
28832 this.fireEvent("beforeshow", this);
28834 this.hidden = false;
28835 this.el.addClass('open');
28837 Roo.get(document).on("mouseup", this.onMouseUp, this);
28839 this.fireEvent("show", this);
28846 this.fireEvent("beforehide", this);
28848 this.hidden = true;
28849 this.el.removeClass('open');
28851 Roo.get(document).un("mouseup", this.onMouseUp);
28853 this.fireEvent("hide", this);
28856 onMouseUp : function()
28870 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28873 * @class Roo.bootstrap.menu.Item
28874 * @extends Roo.bootstrap.Component
28875 * Bootstrap MenuItem class
28876 * @cfg {Boolean} submenu (true | false) default false
28877 * @cfg {String} html text of the item
28878 * @cfg {String} href the link
28879 * @cfg {Boolean} disable (true | false) default false
28880 * @cfg {Boolean} preventDefault (true | false) default true
28881 * @cfg {String} icon Font awesome icon
28882 * @cfg {String} pos Submenu align to (left | right) default right
28886 * Create a new Item
28887 * @param {Object} config The config object
28891 Roo.bootstrap.menu.Item = function(config){
28892 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28896 * Fires when the mouse is hovering over this menu
28897 * @param {Roo.bootstrap.menu.Item} this
28898 * @param {Roo.EventObject} e
28903 * Fires when the mouse exits this menu
28904 * @param {Roo.bootstrap.menu.Item} this
28905 * @param {Roo.EventObject} e
28911 * The raw click event for the entire grid.
28912 * @param {Roo.EventObject} e
28918 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28923 preventDefault: true,
28928 getAutoCreate : function()
28933 cls : 'roo-menu-item-text',
28941 cls : 'fa ' + this.icon
28950 href : this.href || '#',
28957 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28961 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28963 if(this.pos == 'left'){
28964 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28971 initEvents : function()
28973 this.el.on('mouseover', this.onMouseOver, this);
28974 this.el.on('mouseout', this.onMouseOut, this);
28976 this.el.select('a', true).first().on('click', this.onClick, this);
28980 onClick : function(e)
28982 if(this.preventDefault){
28983 e.preventDefault();
28986 this.fireEvent("click", this, e);
28989 onMouseOver : function(e)
28991 if(this.submenu && this.pos == 'left'){
28992 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28995 this.fireEvent("mouseover", this, e);
28998 onMouseOut : function(e)
29000 this.fireEvent("mouseout", this, e);
29012 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29015 * @class Roo.bootstrap.menu.Separator
29016 * @extends Roo.bootstrap.Component
29017 * Bootstrap Separator class
29020 * Create a new Separator
29021 * @param {Object} config The config object
29025 Roo.bootstrap.menu.Separator = function(config){
29026 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29029 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29031 getAutoCreate : function(){
29034 cls: 'dropdown-divider divider'
29052 * @class Roo.bootstrap.Tooltip
29053 * Bootstrap Tooltip class
29054 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29055 * to determine which dom element triggers the tooltip.
29057 * It needs to add support for additional attributes like tooltip-position
29060 * Create a new Toolti
29061 * @param {Object} config The config object
29064 Roo.bootstrap.Tooltip = function(config){
29065 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29067 this.alignment = Roo.bootstrap.Tooltip.alignment;
29069 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29070 this.alignment = config.alignment;
29075 Roo.apply(Roo.bootstrap.Tooltip, {
29077 * @function init initialize tooltip monitoring.
29081 currentTip : false,
29082 currentRegion : false,
29088 Roo.get(document).on('mouseover', this.enter ,this);
29089 Roo.get(document).on('mouseout', this.leave, this);
29092 this.currentTip = new Roo.bootstrap.Tooltip();
29095 enter : function(ev)
29097 var dom = ev.getTarget();
29099 //Roo.log(['enter',dom]);
29100 var el = Roo.fly(dom);
29101 if (this.currentEl) {
29103 //Roo.log(this.currentEl);
29104 //Roo.log(this.currentEl.contains(dom));
29105 if (this.currentEl == el) {
29108 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29114 if (this.currentTip.el) {
29115 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29119 if(!el || el.dom == document){
29125 if (!el.attr('tooltip')) {
29126 pel = el.findParent("[tooltip]");
29128 bindEl = Roo.get(pel);
29134 // you can not look for children, as if el is the body.. then everythign is the child..
29135 if (!pel && !el.attr('tooltip')) { //
29136 if (!el.select("[tooltip]").elements.length) {
29139 // is the mouse over this child...?
29140 bindEl = el.select("[tooltip]").first();
29141 var xy = ev.getXY();
29142 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29143 //Roo.log("not in region.");
29146 //Roo.log("child element over..");
29149 this.currentEl = el;
29150 this.currentTip.bind(bindEl);
29151 this.currentRegion = Roo.lib.Region.getRegion(dom);
29152 this.currentTip.enter();
29155 leave : function(ev)
29157 var dom = ev.getTarget();
29158 //Roo.log(['leave',dom]);
29159 if (!this.currentEl) {
29164 if (dom != this.currentEl.dom) {
29167 var xy = ev.getXY();
29168 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29171 // only activate leave if mouse cursor is outside... bounding box..
29176 if (this.currentTip) {
29177 this.currentTip.leave();
29179 //Roo.log('clear currentEl');
29180 this.currentEl = false;
29185 'left' : ['r-l', [-2,0], 'right'],
29186 'right' : ['l-r', [2,0], 'left'],
29187 'bottom' : ['t-b', [0,2], 'top'],
29188 'top' : [ 'b-t', [0,-2], 'bottom']
29194 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29199 delay : null, // can be { show : 300 , hide: 500}
29203 hoverState : null, //???
29205 placement : 'bottom',
29209 getAutoCreate : function(){
29216 cls : 'tooltip-arrow arrow'
29219 cls : 'tooltip-inner'
29226 bind : function(el)
29231 initEvents : function()
29233 this.arrowEl = this.el.select('.arrow', true).first();
29234 this.innerEl = this.el.select('.tooltip-inner', true).first();
29237 enter : function () {
29239 if (this.timeout != null) {
29240 clearTimeout(this.timeout);
29243 this.hoverState = 'in';
29244 //Roo.log("enter - show");
29245 if (!this.delay || !this.delay.show) {
29250 this.timeout = setTimeout(function () {
29251 if (_t.hoverState == 'in') {
29254 }, this.delay.show);
29258 clearTimeout(this.timeout);
29260 this.hoverState = 'out';
29261 if (!this.delay || !this.delay.hide) {
29267 this.timeout = setTimeout(function () {
29268 //Roo.log("leave - timeout");
29270 if (_t.hoverState == 'out') {
29272 Roo.bootstrap.Tooltip.currentEl = false;
29277 show : function (msg)
29280 this.render(document.body);
29283 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29285 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29287 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29289 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29290 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29292 var placement = typeof this.placement == 'function' ?
29293 this.placement.call(this, this.el, on_el) :
29296 var autoToken = /\s?auto?\s?/i;
29297 var autoPlace = autoToken.test(placement);
29299 placement = placement.replace(autoToken, '') || 'top';
29303 //this.el.setXY([0,0]);
29305 //this.el.dom.style.display='block';
29307 //this.el.appendTo(on_el);
29309 var p = this.getPosition();
29310 var box = this.el.getBox();
29316 var align = this.alignment[placement];
29318 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29320 if(placement == 'top' || placement == 'bottom'){
29322 placement = 'right';
29325 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29326 placement = 'left';
29329 var scroll = Roo.select('body', true).first().getScroll();
29331 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29335 align = this.alignment[placement];
29337 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29341 var elems = document.getElementsByTagName('div');
29342 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29343 for (var i = 0; i < elems.length; i++) {
29344 var zindex = Number.parseInt(
29345 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29348 if (zindex > highest) {
29355 this.el.dom.style.zIndex = highest;
29357 this.el.alignTo(this.bindEl, align[0],align[1]);
29358 //var arrow = this.el.select('.arrow',true).first();
29359 //arrow.set(align[2],
29361 this.el.addClass(placement);
29362 this.el.addClass("bs-tooltip-"+ placement);
29364 this.el.addClass('in fade show');
29366 this.hoverState = null;
29368 if (this.el.hasClass('fade')) {
29383 //this.el.setXY([0,0]);
29384 this.el.removeClass(['show', 'in']);
29400 * @class Roo.bootstrap.LocationPicker
29401 * @extends Roo.bootstrap.Component
29402 * Bootstrap LocationPicker class
29403 * @cfg {Number} latitude Position when init default 0
29404 * @cfg {Number} longitude Position when init default 0
29405 * @cfg {Number} zoom default 15
29406 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29407 * @cfg {Boolean} mapTypeControl default false
29408 * @cfg {Boolean} disableDoubleClickZoom default false
29409 * @cfg {Boolean} scrollwheel default true
29410 * @cfg {Boolean} streetViewControl default false
29411 * @cfg {Number} radius default 0
29412 * @cfg {String} locationName
29413 * @cfg {Boolean} draggable default true
29414 * @cfg {Boolean} enableAutocomplete default false
29415 * @cfg {Boolean} enableReverseGeocode default true
29416 * @cfg {String} markerTitle
29419 * Create a new LocationPicker
29420 * @param {Object} config The config object
29424 Roo.bootstrap.LocationPicker = function(config){
29426 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29431 * Fires when the picker initialized.
29432 * @param {Roo.bootstrap.LocationPicker} this
29433 * @param {Google Location} location
29437 * @event positionchanged
29438 * Fires when the picker position changed.
29439 * @param {Roo.bootstrap.LocationPicker} this
29440 * @param {Google Location} location
29442 positionchanged : true,
29445 * Fires when the map resize.
29446 * @param {Roo.bootstrap.LocationPicker} this
29451 * Fires when the map show.
29452 * @param {Roo.bootstrap.LocationPicker} this
29457 * Fires when the map hide.
29458 * @param {Roo.bootstrap.LocationPicker} this
29463 * Fires when click the map.
29464 * @param {Roo.bootstrap.LocationPicker} this
29465 * @param {Map event} e
29469 * @event mapRightClick
29470 * Fires when right click the map.
29471 * @param {Roo.bootstrap.LocationPicker} this
29472 * @param {Map event} e
29474 mapRightClick : true,
29476 * @event markerClick
29477 * Fires when click the marker.
29478 * @param {Roo.bootstrap.LocationPicker} this
29479 * @param {Map event} e
29481 markerClick : true,
29483 * @event markerRightClick
29484 * Fires when right click the marker.
29485 * @param {Roo.bootstrap.LocationPicker} this
29486 * @param {Map event} e
29488 markerRightClick : true,
29490 * @event OverlayViewDraw
29491 * Fires when OverlayView Draw
29492 * @param {Roo.bootstrap.LocationPicker} this
29494 OverlayViewDraw : true,
29496 * @event OverlayViewOnAdd
29497 * Fires when OverlayView Draw
29498 * @param {Roo.bootstrap.LocationPicker} this
29500 OverlayViewOnAdd : true,
29502 * @event OverlayViewOnRemove
29503 * Fires when OverlayView Draw
29504 * @param {Roo.bootstrap.LocationPicker} this
29506 OverlayViewOnRemove : true,
29508 * @event OverlayViewShow
29509 * Fires when OverlayView Draw
29510 * @param {Roo.bootstrap.LocationPicker} this
29511 * @param {Pixel} cpx
29513 OverlayViewShow : true,
29515 * @event OverlayViewHide
29516 * Fires when OverlayView Draw
29517 * @param {Roo.bootstrap.LocationPicker} this
29519 OverlayViewHide : true,
29521 * @event loadexception
29522 * Fires when load google lib failed.
29523 * @param {Roo.bootstrap.LocationPicker} this
29525 loadexception : true
29530 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29532 gMapContext: false,
29538 mapTypeControl: false,
29539 disableDoubleClickZoom: false,
29541 streetViewControl: false,
29545 enableAutocomplete: false,
29546 enableReverseGeocode: true,
29549 getAutoCreate: function()
29554 cls: 'roo-location-picker'
29560 initEvents: function(ct, position)
29562 if(!this.el.getWidth() || this.isApplied()){
29566 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29571 initial: function()
29573 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29574 this.fireEvent('loadexception', this);
29578 if(!this.mapTypeId){
29579 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29582 this.gMapContext = this.GMapContext();
29584 this.initOverlayView();
29586 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29590 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29591 _this.setPosition(_this.gMapContext.marker.position);
29594 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29595 _this.fireEvent('mapClick', this, event);
29599 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29600 _this.fireEvent('mapRightClick', this, event);
29604 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29605 _this.fireEvent('markerClick', this, event);
29609 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29610 _this.fireEvent('markerRightClick', this, event);
29614 this.setPosition(this.gMapContext.location);
29616 this.fireEvent('initial', this, this.gMapContext.location);
29619 initOverlayView: function()
29623 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29627 _this.fireEvent('OverlayViewDraw', _this);
29632 _this.fireEvent('OverlayViewOnAdd', _this);
29635 onRemove: function()
29637 _this.fireEvent('OverlayViewOnRemove', _this);
29640 show: function(cpx)
29642 _this.fireEvent('OverlayViewShow', _this, cpx);
29647 _this.fireEvent('OverlayViewHide', _this);
29653 fromLatLngToContainerPixel: function(event)
29655 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29658 isApplied: function()
29660 return this.getGmapContext() == false ? false : true;
29663 getGmapContext: function()
29665 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29668 GMapContext: function()
29670 var position = new google.maps.LatLng(this.latitude, this.longitude);
29672 var _map = new google.maps.Map(this.el.dom, {
29675 mapTypeId: this.mapTypeId,
29676 mapTypeControl: this.mapTypeControl,
29677 disableDoubleClickZoom: this.disableDoubleClickZoom,
29678 scrollwheel: this.scrollwheel,
29679 streetViewControl: this.streetViewControl,
29680 locationName: this.locationName,
29681 draggable: this.draggable,
29682 enableAutocomplete: this.enableAutocomplete,
29683 enableReverseGeocode: this.enableReverseGeocode
29686 var _marker = new google.maps.Marker({
29687 position: position,
29689 title: this.markerTitle,
29690 draggable: this.draggable
29697 location: position,
29698 radius: this.radius,
29699 locationName: this.locationName,
29700 addressComponents: {
29701 formatted_address: null,
29702 addressLine1: null,
29703 addressLine2: null,
29705 streetNumber: null,
29709 stateOrProvince: null
29712 domContainer: this.el.dom,
29713 geodecoder: new google.maps.Geocoder()
29717 drawCircle: function(center, radius, options)
29719 if (this.gMapContext.circle != null) {
29720 this.gMapContext.circle.setMap(null);
29724 options = Roo.apply({}, options, {
29725 strokeColor: "#0000FF",
29726 strokeOpacity: .35,
29728 fillColor: "#0000FF",
29732 options.map = this.gMapContext.map;
29733 options.radius = radius;
29734 options.center = center;
29735 this.gMapContext.circle = new google.maps.Circle(options);
29736 return this.gMapContext.circle;
29742 setPosition: function(location)
29744 this.gMapContext.location = location;
29745 this.gMapContext.marker.setPosition(location);
29746 this.gMapContext.map.panTo(location);
29747 this.drawCircle(location, this.gMapContext.radius, {});
29751 if (this.gMapContext.settings.enableReverseGeocode) {
29752 this.gMapContext.geodecoder.geocode({
29753 latLng: this.gMapContext.location
29754 }, function(results, status) {
29756 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29757 _this.gMapContext.locationName = results[0].formatted_address;
29758 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29760 _this.fireEvent('positionchanged', this, location);
29767 this.fireEvent('positionchanged', this, location);
29772 google.maps.event.trigger(this.gMapContext.map, "resize");
29774 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29776 this.fireEvent('resize', this);
29779 setPositionByLatLng: function(latitude, longitude)
29781 this.setPosition(new google.maps.LatLng(latitude, longitude));
29784 getCurrentPosition: function()
29787 latitude: this.gMapContext.location.lat(),
29788 longitude: this.gMapContext.location.lng()
29792 getAddressName: function()
29794 return this.gMapContext.locationName;
29797 getAddressComponents: function()
29799 return this.gMapContext.addressComponents;
29802 address_component_from_google_geocode: function(address_components)
29806 for (var i = 0; i < address_components.length; i++) {
29807 var component = address_components[i];
29808 if (component.types.indexOf("postal_code") >= 0) {
29809 result.postalCode = component.short_name;
29810 } else if (component.types.indexOf("street_number") >= 0) {
29811 result.streetNumber = component.short_name;
29812 } else if (component.types.indexOf("route") >= 0) {
29813 result.streetName = component.short_name;
29814 } else if (component.types.indexOf("neighborhood") >= 0) {
29815 result.city = component.short_name;
29816 } else if (component.types.indexOf("locality") >= 0) {
29817 result.city = component.short_name;
29818 } else if (component.types.indexOf("sublocality") >= 0) {
29819 result.district = component.short_name;
29820 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29821 result.stateOrProvince = component.short_name;
29822 } else if (component.types.indexOf("country") >= 0) {
29823 result.country = component.short_name;
29827 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29828 result.addressLine2 = "";
29832 setZoomLevel: function(zoom)
29834 this.gMapContext.map.setZoom(zoom);
29847 this.fireEvent('show', this);
29858 this.fireEvent('hide', this);
29863 Roo.apply(Roo.bootstrap.LocationPicker, {
29865 OverlayView : function(map, options)
29867 options = options || {};
29874 * @class Roo.bootstrap.Alert
29875 * @extends Roo.bootstrap.Component
29876 * Bootstrap Alert class - shows an alert area box
29878 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29879 Enter a valid email address
29882 * @cfg {String} title The title of alert
29883 * @cfg {String} html The content of alert
29884 * @cfg {String} weight ( success | info | warning | danger )
29885 * @cfg {String} fa font-awesomeicon
29886 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29887 * @cfg {Boolean} close true to show a x closer
29891 * Create a new alert
29892 * @param {Object} config The config object
29896 Roo.bootstrap.Alert = function(config){
29897 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29901 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29907 faicon: false, // BC
29911 getAutoCreate : function()
29923 style : this.close ? '' : 'display:none'
29927 cls : 'roo-alert-icon'
29932 cls : 'roo-alert-title',
29937 cls : 'roo-alert-text',
29944 cfg.cn[0].cls += ' fa ' + this.faicon;
29947 cfg.cn[0].cls += ' fa ' + this.fa;
29951 cfg.cls += ' alert-' + this.weight;
29957 initEvents: function()
29959 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29960 this.titleEl = this.el.select('.roo-alert-title',true).first();
29961 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29962 if (this.seconds > 0) {
29963 this.hide.defer(this.seconds, this);
29967 setTitle : function(str)
29969 this.titleEl.dom.innerHTML = str;
29972 setText : function(str)
29974 this.titleEl.dom.innerHTML = str;
29977 setWeight : function(weight)
29980 this.el.removeClass('alert-' + this.weight);
29983 this.weight = weight;
29985 this.el.addClass('alert-' + this.weight);
29988 setIcon : function(icon)
29991 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29994 this.faicon = icon;
29996 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30017 * @class Roo.bootstrap.UploadCropbox
30018 * @extends Roo.bootstrap.Component
30019 * Bootstrap UploadCropbox class
30020 * @cfg {String} emptyText show when image has been loaded
30021 * @cfg {String} rotateNotify show when image too small to rotate
30022 * @cfg {Number} errorTimeout default 3000
30023 * @cfg {Number} minWidth default 300
30024 * @cfg {Number} minHeight default 300
30025 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30026 * @cfg {Boolean} isDocument (true|false) default false
30027 * @cfg {String} url action url
30028 * @cfg {String} paramName default 'imageUpload'
30029 * @cfg {String} method default POST
30030 * @cfg {Boolean} loadMask (true|false) default true
30031 * @cfg {Boolean} loadingText default 'Loading...'
30034 * Create a new UploadCropbox
30035 * @param {Object} config The config object
30038 Roo.bootstrap.UploadCropbox = function(config){
30039 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30043 * @event beforeselectfile
30044 * Fire before select file
30045 * @param {Roo.bootstrap.UploadCropbox} this
30047 "beforeselectfile" : true,
30050 * Fire after initEvent
30051 * @param {Roo.bootstrap.UploadCropbox} this
30056 * Fire after initEvent
30057 * @param {Roo.bootstrap.UploadCropbox} this
30058 * @param {String} data
30063 * Fire when preparing the file data
30064 * @param {Roo.bootstrap.UploadCropbox} this
30065 * @param {Object} file
30070 * Fire when get exception
30071 * @param {Roo.bootstrap.UploadCropbox} this
30072 * @param {XMLHttpRequest} xhr
30074 "exception" : true,
30076 * @event beforeloadcanvas
30077 * Fire before load the canvas
30078 * @param {Roo.bootstrap.UploadCropbox} this
30079 * @param {String} src
30081 "beforeloadcanvas" : true,
30084 * Fire when trash image
30085 * @param {Roo.bootstrap.UploadCropbox} this
30090 * Fire when download the image
30091 * @param {Roo.bootstrap.UploadCropbox} this
30095 * @event footerbuttonclick
30096 * Fire when footerbuttonclick
30097 * @param {Roo.bootstrap.UploadCropbox} this
30098 * @param {String} type
30100 "footerbuttonclick" : true,
30104 * @param {Roo.bootstrap.UploadCropbox} this
30109 * Fire when rotate the image
30110 * @param {Roo.bootstrap.UploadCropbox} this
30111 * @param {String} pos
30116 * Fire when inspect the file
30117 * @param {Roo.bootstrap.UploadCropbox} this
30118 * @param {Object} file
30123 * Fire when xhr upload the file
30124 * @param {Roo.bootstrap.UploadCropbox} this
30125 * @param {Object} data
30130 * Fire when arrange the file data
30131 * @param {Roo.bootstrap.UploadCropbox} this
30132 * @param {Object} formData
30137 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30140 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30142 emptyText : 'Click to upload image',
30143 rotateNotify : 'Image is too small to rotate',
30144 errorTimeout : 3000,
30158 cropType : 'image/jpeg',
30160 canvasLoaded : false,
30161 isDocument : false,
30163 paramName : 'imageUpload',
30165 loadingText : 'Loading...',
30168 getAutoCreate : function()
30172 cls : 'roo-upload-cropbox',
30176 cls : 'roo-upload-cropbox-selector',
30181 cls : 'roo-upload-cropbox-body',
30182 style : 'cursor:pointer',
30186 cls : 'roo-upload-cropbox-preview'
30190 cls : 'roo-upload-cropbox-thumb'
30194 cls : 'roo-upload-cropbox-empty-notify',
30195 html : this.emptyText
30199 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30200 html : this.rotateNotify
30206 cls : 'roo-upload-cropbox-footer',
30209 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30219 onRender : function(ct, position)
30221 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30223 if (this.buttons.length) {
30225 Roo.each(this.buttons, function(bb) {
30227 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30229 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30235 this.maskEl = this.el;
30239 initEvents : function()
30241 this.urlAPI = (window.createObjectURL && window) ||
30242 (window.URL && URL.revokeObjectURL && URL) ||
30243 (window.webkitURL && webkitURL);
30245 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30246 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30248 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30249 this.selectorEl.hide();
30251 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30252 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30254 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30255 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30256 this.thumbEl.hide();
30258 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30259 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30261 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30262 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30263 this.errorEl.hide();
30265 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30266 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30267 this.footerEl.hide();
30269 this.setThumbBoxSize();
30275 this.fireEvent('initial', this);
30282 window.addEventListener("resize", function() { _this.resize(); } );
30284 this.bodyEl.on('click', this.beforeSelectFile, this);
30287 this.bodyEl.on('touchstart', this.onTouchStart, this);
30288 this.bodyEl.on('touchmove', this.onTouchMove, this);
30289 this.bodyEl.on('touchend', this.onTouchEnd, this);
30293 this.bodyEl.on('mousedown', this.onMouseDown, this);
30294 this.bodyEl.on('mousemove', this.onMouseMove, this);
30295 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30296 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30297 Roo.get(document).on('mouseup', this.onMouseUp, this);
30300 this.selectorEl.on('change', this.onFileSelected, this);
30306 this.baseScale = 1;
30308 this.baseRotate = 1;
30309 this.dragable = false;
30310 this.pinching = false;
30313 this.cropData = false;
30314 this.notifyEl.dom.innerHTML = this.emptyText;
30316 this.selectorEl.dom.value = '';
30320 resize : function()
30322 if(this.fireEvent('resize', this) != false){
30323 this.setThumbBoxPosition();
30324 this.setCanvasPosition();
30328 onFooterButtonClick : function(e, el, o, type)
30331 case 'rotate-left' :
30332 this.onRotateLeft(e);
30334 case 'rotate-right' :
30335 this.onRotateRight(e);
30338 this.beforeSelectFile(e);
30353 this.fireEvent('footerbuttonclick', this, type);
30356 beforeSelectFile : function(e)
30358 e.preventDefault();
30360 if(this.fireEvent('beforeselectfile', this) != false){
30361 this.selectorEl.dom.click();
30365 onFileSelected : function(e)
30367 e.preventDefault();
30369 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30373 var file = this.selectorEl.dom.files[0];
30375 if(this.fireEvent('inspect', this, file) != false){
30376 this.prepare(file);
30381 trash : function(e)
30383 this.fireEvent('trash', this);
30386 download : function(e)
30388 this.fireEvent('download', this);
30391 loadCanvas : function(src)
30393 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30397 this.imageEl = document.createElement('img');
30401 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30403 this.imageEl.src = src;
30407 onLoadCanvas : function()
30409 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30410 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30412 this.bodyEl.un('click', this.beforeSelectFile, this);
30414 this.notifyEl.hide();
30415 this.thumbEl.show();
30416 this.footerEl.show();
30418 this.baseRotateLevel();
30420 if(this.isDocument){
30421 this.setThumbBoxSize();
30424 this.setThumbBoxPosition();
30426 this.baseScaleLevel();
30432 this.canvasLoaded = true;
30435 this.maskEl.unmask();
30440 setCanvasPosition : function()
30442 if(!this.canvasEl){
30446 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30447 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30449 this.previewEl.setLeft(pw);
30450 this.previewEl.setTop(ph);
30454 onMouseDown : function(e)
30458 this.dragable = true;
30459 this.pinching = false;
30461 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30462 this.dragable = false;
30466 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30467 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30471 onMouseMove : function(e)
30475 if(!this.canvasLoaded){
30479 if (!this.dragable){
30483 var minX = Math.ceil(this.thumbEl.getLeft(true));
30484 var minY = Math.ceil(this.thumbEl.getTop(true));
30486 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30487 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30489 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30490 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30492 x = x - this.mouseX;
30493 y = y - this.mouseY;
30495 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30496 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30498 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30499 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30501 this.previewEl.setLeft(bgX);
30502 this.previewEl.setTop(bgY);
30504 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30505 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30508 onMouseUp : function(e)
30512 this.dragable = false;
30515 onMouseWheel : function(e)
30519 this.startScale = this.scale;
30521 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30523 if(!this.zoomable()){
30524 this.scale = this.startScale;
30533 zoomable : function()
30535 var minScale = this.thumbEl.getWidth() / this.minWidth;
30537 if(this.minWidth < this.minHeight){
30538 minScale = this.thumbEl.getHeight() / this.minHeight;
30541 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30542 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30546 (this.rotate == 0 || this.rotate == 180) &&
30548 width > this.imageEl.OriginWidth ||
30549 height > this.imageEl.OriginHeight ||
30550 (width < this.minWidth && height < this.minHeight)
30558 (this.rotate == 90 || this.rotate == 270) &&
30560 width > this.imageEl.OriginWidth ||
30561 height > this.imageEl.OriginHeight ||
30562 (width < this.minHeight && height < this.minWidth)
30569 !this.isDocument &&
30570 (this.rotate == 0 || this.rotate == 180) &&
30572 width < this.minWidth ||
30573 width > this.imageEl.OriginWidth ||
30574 height < this.minHeight ||
30575 height > this.imageEl.OriginHeight
30582 !this.isDocument &&
30583 (this.rotate == 90 || this.rotate == 270) &&
30585 width < this.minHeight ||
30586 width > this.imageEl.OriginWidth ||
30587 height < this.minWidth ||
30588 height > this.imageEl.OriginHeight
30598 onRotateLeft : function(e)
30600 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30602 var minScale = this.thumbEl.getWidth() / this.minWidth;
30604 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30605 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30607 this.startScale = this.scale;
30609 while (this.getScaleLevel() < minScale){
30611 this.scale = this.scale + 1;
30613 if(!this.zoomable()){
30618 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30619 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30624 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30631 this.scale = this.startScale;
30633 this.onRotateFail();
30638 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30640 if(this.isDocument){
30641 this.setThumbBoxSize();
30642 this.setThumbBoxPosition();
30643 this.setCanvasPosition();
30648 this.fireEvent('rotate', this, 'left');
30652 onRotateRight : function(e)
30654 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30656 var minScale = this.thumbEl.getWidth() / this.minWidth;
30658 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30659 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30661 this.startScale = this.scale;
30663 while (this.getScaleLevel() < minScale){
30665 this.scale = this.scale + 1;
30667 if(!this.zoomable()){
30672 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30673 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30678 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30685 this.scale = this.startScale;
30687 this.onRotateFail();
30692 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30694 if(this.isDocument){
30695 this.setThumbBoxSize();
30696 this.setThumbBoxPosition();
30697 this.setCanvasPosition();
30702 this.fireEvent('rotate', this, 'right');
30705 onRotateFail : function()
30707 this.errorEl.show(true);
30711 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30716 this.previewEl.dom.innerHTML = '';
30718 var canvasEl = document.createElement("canvas");
30720 var contextEl = canvasEl.getContext("2d");
30722 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30723 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30724 var center = this.imageEl.OriginWidth / 2;
30726 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30727 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30728 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30729 center = this.imageEl.OriginHeight / 2;
30732 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30734 contextEl.translate(center, center);
30735 contextEl.rotate(this.rotate * Math.PI / 180);
30737 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30739 this.canvasEl = document.createElement("canvas");
30741 this.contextEl = this.canvasEl.getContext("2d");
30743 switch (this.rotate) {
30746 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30747 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30749 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30754 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30755 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30757 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30758 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);
30762 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30767 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30768 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30770 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30771 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);
30775 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);
30780 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30781 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30783 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30784 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30788 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);
30795 this.previewEl.appendChild(this.canvasEl);
30797 this.setCanvasPosition();
30802 if(!this.canvasLoaded){
30806 var imageCanvas = document.createElement("canvas");
30808 var imageContext = imageCanvas.getContext("2d");
30810 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30811 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30813 var center = imageCanvas.width / 2;
30815 imageContext.translate(center, center);
30817 imageContext.rotate(this.rotate * Math.PI / 180);
30819 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30821 var canvas = document.createElement("canvas");
30823 var context = canvas.getContext("2d");
30825 canvas.width = this.minWidth;
30826 canvas.height = this.minHeight;
30828 switch (this.rotate) {
30831 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30832 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30834 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30835 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30837 var targetWidth = this.minWidth - 2 * x;
30838 var targetHeight = this.minHeight - 2 * y;
30842 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30843 scale = targetWidth / width;
30846 if(x > 0 && y == 0){
30847 scale = targetHeight / height;
30850 if(x > 0 && y > 0){
30851 scale = targetWidth / width;
30853 if(width < height){
30854 scale = targetHeight / height;
30858 context.scale(scale, scale);
30860 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30861 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30863 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30864 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30866 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30871 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30872 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30874 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30875 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30877 var targetWidth = this.minWidth - 2 * x;
30878 var targetHeight = this.minHeight - 2 * y;
30882 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30883 scale = targetWidth / width;
30886 if(x > 0 && y == 0){
30887 scale = targetHeight / height;
30890 if(x > 0 && y > 0){
30891 scale = targetWidth / width;
30893 if(width < height){
30894 scale = targetHeight / height;
30898 context.scale(scale, scale);
30900 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30901 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30903 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30904 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30906 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30908 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30913 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30914 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30916 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30917 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30919 var targetWidth = this.minWidth - 2 * x;
30920 var targetHeight = this.minHeight - 2 * y;
30924 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30925 scale = targetWidth / width;
30928 if(x > 0 && y == 0){
30929 scale = targetHeight / height;
30932 if(x > 0 && y > 0){
30933 scale = targetWidth / width;
30935 if(width < height){
30936 scale = targetHeight / height;
30940 context.scale(scale, scale);
30942 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30943 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30945 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30946 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30948 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30949 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30951 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30956 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30957 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30959 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30960 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30962 var targetWidth = this.minWidth - 2 * x;
30963 var targetHeight = this.minHeight - 2 * y;
30967 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30968 scale = targetWidth / width;
30971 if(x > 0 && y == 0){
30972 scale = targetHeight / height;
30975 if(x > 0 && y > 0){
30976 scale = targetWidth / width;
30978 if(width < height){
30979 scale = targetHeight / height;
30983 context.scale(scale, scale);
30985 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30986 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30988 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30989 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30991 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30993 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31000 this.cropData = canvas.toDataURL(this.cropType);
31002 if(this.fireEvent('crop', this, this.cropData) !== false){
31003 this.process(this.file, this.cropData);
31010 setThumbBoxSize : function()
31014 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31015 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31016 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31018 this.minWidth = width;
31019 this.minHeight = height;
31021 if(this.rotate == 90 || this.rotate == 270){
31022 this.minWidth = height;
31023 this.minHeight = width;
31028 width = Math.ceil(this.minWidth * height / this.minHeight);
31030 if(this.minWidth > this.minHeight){
31032 height = Math.ceil(this.minHeight * width / this.minWidth);
31035 this.thumbEl.setStyle({
31036 width : width + 'px',
31037 height : height + 'px'
31044 setThumbBoxPosition : function()
31046 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31047 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31049 this.thumbEl.setLeft(x);
31050 this.thumbEl.setTop(y);
31054 baseRotateLevel : function()
31056 this.baseRotate = 1;
31059 typeof(this.exif) != 'undefined' &&
31060 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31061 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31063 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31066 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31070 baseScaleLevel : function()
31074 if(this.isDocument){
31076 if(this.baseRotate == 6 || this.baseRotate == 8){
31078 height = this.thumbEl.getHeight();
31079 this.baseScale = height / this.imageEl.OriginWidth;
31081 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31082 width = this.thumbEl.getWidth();
31083 this.baseScale = width / this.imageEl.OriginHeight;
31089 height = this.thumbEl.getHeight();
31090 this.baseScale = height / this.imageEl.OriginHeight;
31092 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31093 width = this.thumbEl.getWidth();
31094 this.baseScale = width / this.imageEl.OriginWidth;
31100 if(this.baseRotate == 6 || this.baseRotate == 8){
31102 width = this.thumbEl.getHeight();
31103 this.baseScale = width / this.imageEl.OriginHeight;
31105 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31106 height = this.thumbEl.getWidth();
31107 this.baseScale = height / this.imageEl.OriginHeight;
31110 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31111 height = this.thumbEl.getWidth();
31112 this.baseScale = height / this.imageEl.OriginHeight;
31114 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31115 width = this.thumbEl.getHeight();
31116 this.baseScale = width / this.imageEl.OriginWidth;
31123 width = this.thumbEl.getWidth();
31124 this.baseScale = width / this.imageEl.OriginWidth;
31126 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31127 height = this.thumbEl.getHeight();
31128 this.baseScale = height / this.imageEl.OriginHeight;
31131 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31133 height = this.thumbEl.getHeight();
31134 this.baseScale = height / this.imageEl.OriginHeight;
31136 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31137 width = this.thumbEl.getWidth();
31138 this.baseScale = width / this.imageEl.OriginWidth;
31146 getScaleLevel : function()
31148 return this.baseScale * Math.pow(1.1, this.scale);
31151 onTouchStart : function(e)
31153 if(!this.canvasLoaded){
31154 this.beforeSelectFile(e);
31158 var touches = e.browserEvent.touches;
31164 if(touches.length == 1){
31165 this.onMouseDown(e);
31169 if(touches.length != 2){
31175 for(var i = 0, finger; finger = touches[i]; i++){
31176 coords.push(finger.pageX, finger.pageY);
31179 var x = Math.pow(coords[0] - coords[2], 2);
31180 var y = Math.pow(coords[1] - coords[3], 2);
31182 this.startDistance = Math.sqrt(x + y);
31184 this.startScale = this.scale;
31186 this.pinching = true;
31187 this.dragable = false;
31191 onTouchMove : function(e)
31193 if(!this.pinching && !this.dragable){
31197 var touches = e.browserEvent.touches;
31204 this.onMouseMove(e);
31210 for(var i = 0, finger; finger = touches[i]; i++){
31211 coords.push(finger.pageX, finger.pageY);
31214 var x = Math.pow(coords[0] - coords[2], 2);
31215 var y = Math.pow(coords[1] - coords[3], 2);
31217 this.endDistance = Math.sqrt(x + y);
31219 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31221 if(!this.zoomable()){
31222 this.scale = this.startScale;
31230 onTouchEnd : function(e)
31232 this.pinching = false;
31233 this.dragable = false;
31237 process : function(file, crop)
31240 this.maskEl.mask(this.loadingText);
31243 this.xhr = new XMLHttpRequest();
31245 file.xhr = this.xhr;
31247 this.xhr.open(this.method, this.url, true);
31250 "Accept": "application/json",
31251 "Cache-Control": "no-cache",
31252 "X-Requested-With": "XMLHttpRequest"
31255 for (var headerName in headers) {
31256 var headerValue = headers[headerName];
31258 this.xhr.setRequestHeader(headerName, headerValue);
31264 this.xhr.onload = function()
31266 _this.xhrOnLoad(_this.xhr);
31269 this.xhr.onerror = function()
31271 _this.xhrOnError(_this.xhr);
31274 var formData = new FormData();
31276 formData.append('returnHTML', 'NO');
31279 formData.append('crop', crop);
31282 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31283 formData.append(this.paramName, file, file.name);
31286 if(typeof(file.filename) != 'undefined'){
31287 formData.append('filename', file.filename);
31290 if(typeof(file.mimetype) != 'undefined'){
31291 formData.append('mimetype', file.mimetype);
31294 if(this.fireEvent('arrange', this, formData) != false){
31295 this.xhr.send(formData);
31299 xhrOnLoad : function(xhr)
31302 this.maskEl.unmask();
31305 if (xhr.readyState !== 4) {
31306 this.fireEvent('exception', this, xhr);
31310 var response = Roo.decode(xhr.responseText);
31312 if(!response.success){
31313 this.fireEvent('exception', this, xhr);
31317 var response = Roo.decode(xhr.responseText);
31319 this.fireEvent('upload', this, response);
31323 xhrOnError : function()
31326 this.maskEl.unmask();
31329 Roo.log('xhr on error');
31331 var response = Roo.decode(xhr.responseText);
31337 prepare : function(file)
31340 this.maskEl.mask(this.loadingText);
31346 if(typeof(file) === 'string'){
31347 this.loadCanvas(file);
31351 if(!file || !this.urlAPI){
31356 this.cropType = file.type;
31360 if(this.fireEvent('prepare', this, this.file) != false){
31362 var reader = new FileReader();
31364 reader.onload = function (e) {
31365 if (e.target.error) {
31366 Roo.log(e.target.error);
31370 var buffer = e.target.result,
31371 dataView = new DataView(buffer),
31373 maxOffset = dataView.byteLength - 4,
31377 if (dataView.getUint16(0) === 0xffd8) {
31378 while (offset < maxOffset) {
31379 markerBytes = dataView.getUint16(offset);
31381 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31382 markerLength = dataView.getUint16(offset + 2) + 2;
31383 if (offset + markerLength > dataView.byteLength) {
31384 Roo.log('Invalid meta data: Invalid segment size.');
31388 if(markerBytes == 0xffe1){
31389 _this.parseExifData(
31396 offset += markerLength;
31406 var url = _this.urlAPI.createObjectURL(_this.file);
31408 _this.loadCanvas(url);
31413 reader.readAsArrayBuffer(this.file);
31419 parseExifData : function(dataView, offset, length)
31421 var tiffOffset = offset + 10,
31425 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31426 // No Exif data, might be XMP data instead
31430 // Check for the ASCII code for "Exif" (0x45786966):
31431 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31432 // No Exif data, might be XMP data instead
31435 if (tiffOffset + 8 > dataView.byteLength) {
31436 Roo.log('Invalid Exif data: Invalid segment size.');
31439 // Check for the two null bytes:
31440 if (dataView.getUint16(offset + 8) !== 0x0000) {
31441 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31444 // Check the byte alignment:
31445 switch (dataView.getUint16(tiffOffset)) {
31447 littleEndian = true;
31450 littleEndian = false;
31453 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31456 // Check for the TIFF tag marker (0x002A):
31457 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31458 Roo.log('Invalid Exif data: Missing TIFF marker.');
31461 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31462 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31464 this.parseExifTags(
31467 tiffOffset + dirOffset,
31472 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31477 if (dirOffset + 6 > dataView.byteLength) {
31478 Roo.log('Invalid Exif data: Invalid directory offset.');
31481 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31482 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31483 if (dirEndOffset + 4 > dataView.byteLength) {
31484 Roo.log('Invalid Exif data: Invalid directory size.');
31487 for (i = 0; i < tagsNumber; i += 1) {
31491 dirOffset + 2 + 12 * i, // tag offset
31495 // Return the offset to the next directory:
31496 return dataView.getUint32(dirEndOffset, littleEndian);
31499 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31501 var tag = dataView.getUint16(offset, littleEndian);
31503 this.exif[tag] = this.getExifValue(
31507 dataView.getUint16(offset + 2, littleEndian), // tag type
31508 dataView.getUint32(offset + 4, littleEndian), // tag length
31513 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31515 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31524 Roo.log('Invalid Exif data: Invalid tag type.');
31528 tagSize = tagType.size * length;
31529 // Determine if the value is contained in the dataOffset bytes,
31530 // or if the value at the dataOffset is a pointer to the actual data:
31531 dataOffset = tagSize > 4 ?
31532 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31533 if (dataOffset + tagSize > dataView.byteLength) {
31534 Roo.log('Invalid Exif data: Invalid data offset.');
31537 if (length === 1) {
31538 return tagType.getValue(dataView, dataOffset, littleEndian);
31541 for (i = 0; i < length; i += 1) {
31542 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31545 if (tagType.ascii) {
31547 // Concatenate the chars:
31548 for (i = 0; i < values.length; i += 1) {
31550 // Ignore the terminating NULL byte(s):
31551 if (c === '\u0000') {
31563 Roo.apply(Roo.bootstrap.UploadCropbox, {
31565 'Orientation': 0x0112
31569 1: 0, //'top-left',
31571 3: 180, //'bottom-right',
31572 // 4: 'bottom-left',
31574 6: 90, //'right-top',
31575 // 7: 'right-bottom',
31576 8: 270 //'left-bottom'
31580 // byte, 8-bit unsigned int:
31582 getValue: function (dataView, dataOffset) {
31583 return dataView.getUint8(dataOffset);
31587 // ascii, 8-bit byte:
31589 getValue: function (dataView, dataOffset) {
31590 return String.fromCharCode(dataView.getUint8(dataOffset));
31595 // short, 16 bit int:
31597 getValue: function (dataView, dataOffset, littleEndian) {
31598 return dataView.getUint16(dataOffset, littleEndian);
31602 // long, 32 bit int:
31604 getValue: function (dataView, dataOffset, littleEndian) {
31605 return dataView.getUint32(dataOffset, littleEndian);
31609 // rational = two long values, first is numerator, second is denominator:
31611 getValue: function (dataView, dataOffset, littleEndian) {
31612 return dataView.getUint32(dataOffset, littleEndian) /
31613 dataView.getUint32(dataOffset + 4, littleEndian);
31617 // slong, 32 bit signed int:
31619 getValue: function (dataView, dataOffset, littleEndian) {
31620 return dataView.getInt32(dataOffset, littleEndian);
31624 // srational, two slongs, first is numerator, second is denominator:
31626 getValue: function (dataView, dataOffset, littleEndian) {
31627 return dataView.getInt32(dataOffset, littleEndian) /
31628 dataView.getInt32(dataOffset + 4, littleEndian);
31638 cls : 'btn-group roo-upload-cropbox-rotate-left',
31639 action : 'rotate-left',
31643 cls : 'btn btn-default',
31644 html : '<i class="fa fa-undo"></i>'
31650 cls : 'btn-group roo-upload-cropbox-picture',
31651 action : 'picture',
31655 cls : 'btn btn-default',
31656 html : '<i class="fa fa-picture-o"></i>'
31662 cls : 'btn-group roo-upload-cropbox-rotate-right',
31663 action : 'rotate-right',
31667 cls : 'btn btn-default',
31668 html : '<i class="fa fa-repeat"></i>'
31676 cls : 'btn-group roo-upload-cropbox-rotate-left',
31677 action : 'rotate-left',
31681 cls : 'btn btn-default',
31682 html : '<i class="fa fa-undo"></i>'
31688 cls : 'btn-group roo-upload-cropbox-download',
31689 action : 'download',
31693 cls : 'btn btn-default',
31694 html : '<i class="fa fa-download"></i>'
31700 cls : 'btn-group roo-upload-cropbox-crop',
31705 cls : 'btn btn-default',
31706 html : '<i class="fa fa-crop"></i>'
31712 cls : 'btn-group roo-upload-cropbox-trash',
31717 cls : 'btn btn-default',
31718 html : '<i class="fa fa-trash"></i>'
31724 cls : 'btn-group roo-upload-cropbox-rotate-right',
31725 action : 'rotate-right',
31729 cls : 'btn btn-default',
31730 html : '<i class="fa fa-repeat"></i>'
31738 cls : 'btn-group roo-upload-cropbox-rotate-left',
31739 action : 'rotate-left',
31743 cls : 'btn btn-default',
31744 html : '<i class="fa fa-undo"></i>'
31750 cls : 'btn-group roo-upload-cropbox-rotate-right',
31751 action : 'rotate-right',
31755 cls : 'btn btn-default',
31756 html : '<i class="fa fa-repeat"></i>'
31769 * @class Roo.bootstrap.DocumentManager
31770 * @extends Roo.bootstrap.Component
31771 * Bootstrap DocumentManager class
31772 * @cfg {String} paramName default 'imageUpload'
31773 * @cfg {String} toolTipName default 'filename'
31774 * @cfg {String} method default POST
31775 * @cfg {String} url action url
31776 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31777 * @cfg {Boolean} multiple multiple upload default true
31778 * @cfg {Number} thumbSize default 300
31779 * @cfg {String} fieldLabel
31780 * @cfg {Number} labelWidth default 4
31781 * @cfg {String} labelAlign (left|top) default left
31782 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31783 * @cfg {Number} labellg set the width of label (1-12)
31784 * @cfg {Number} labelmd set the width of label (1-12)
31785 * @cfg {Number} labelsm set the width of label (1-12)
31786 * @cfg {Number} labelxs set the width of label (1-12)
31789 * Create a new DocumentManager
31790 * @param {Object} config The config object
31793 Roo.bootstrap.DocumentManager = function(config){
31794 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31797 this.delegates = [];
31802 * Fire when initial the DocumentManager
31803 * @param {Roo.bootstrap.DocumentManager} this
31808 * inspect selected file
31809 * @param {Roo.bootstrap.DocumentManager} this
31810 * @param {File} file
31815 * Fire when xhr load exception
31816 * @param {Roo.bootstrap.DocumentManager} this
31817 * @param {XMLHttpRequest} xhr
31819 "exception" : true,
31821 * @event afterupload
31822 * Fire when xhr load exception
31823 * @param {Roo.bootstrap.DocumentManager} this
31824 * @param {XMLHttpRequest} xhr
31826 "afterupload" : true,
31829 * prepare the form data
31830 * @param {Roo.bootstrap.DocumentManager} this
31831 * @param {Object} formData
31836 * Fire when remove the file
31837 * @param {Roo.bootstrap.DocumentManager} this
31838 * @param {Object} file
31843 * Fire after refresh the file
31844 * @param {Roo.bootstrap.DocumentManager} this
31849 * Fire after click the image
31850 * @param {Roo.bootstrap.DocumentManager} this
31851 * @param {Object} file
31856 * Fire when upload a image and editable set to true
31857 * @param {Roo.bootstrap.DocumentManager} this
31858 * @param {Object} file
31862 * @event beforeselectfile
31863 * Fire before select file
31864 * @param {Roo.bootstrap.DocumentManager} this
31866 "beforeselectfile" : true,
31869 * Fire before process file
31870 * @param {Roo.bootstrap.DocumentManager} this
31871 * @param {Object} file
31875 * @event previewrendered
31876 * Fire when preview rendered
31877 * @param {Roo.bootstrap.DocumentManager} this
31878 * @param {Object} file
31880 "previewrendered" : true,
31883 "previewResize" : true
31888 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31897 paramName : 'imageUpload',
31898 toolTipName : 'filename',
31901 labelAlign : 'left',
31911 getAutoCreate : function()
31913 var managerWidget = {
31915 cls : 'roo-document-manager',
31919 cls : 'roo-document-manager-selector',
31924 cls : 'roo-document-manager-uploader',
31928 cls : 'roo-document-manager-upload-btn',
31929 html : '<i class="fa fa-plus"></i>'
31940 cls : 'column col-md-12',
31945 if(this.fieldLabel.length){
31950 cls : 'column col-md-12',
31951 html : this.fieldLabel
31955 cls : 'column col-md-12',
31960 if(this.labelAlign == 'left'){
31965 html : this.fieldLabel
31974 if(this.labelWidth > 12){
31975 content[0].style = "width: " + this.labelWidth + 'px';
31978 if(this.labelWidth < 13 && this.labelmd == 0){
31979 this.labelmd = this.labelWidth;
31982 if(this.labellg > 0){
31983 content[0].cls += ' col-lg-' + this.labellg;
31984 content[1].cls += ' col-lg-' + (12 - this.labellg);
31987 if(this.labelmd > 0){
31988 content[0].cls += ' col-md-' + this.labelmd;
31989 content[1].cls += ' col-md-' + (12 - this.labelmd);
31992 if(this.labelsm > 0){
31993 content[0].cls += ' col-sm-' + this.labelsm;
31994 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31997 if(this.labelxs > 0){
31998 content[0].cls += ' col-xs-' + this.labelxs;
31999 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32007 cls : 'row clearfix',
32015 initEvents : function()
32017 this.managerEl = this.el.select('.roo-document-manager', true).first();
32018 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32020 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32021 this.selectorEl.hide();
32024 this.selectorEl.attr('multiple', 'multiple');
32027 this.selectorEl.on('change', this.onFileSelected, this);
32029 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32030 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32032 this.uploader.on('click', this.onUploaderClick, this);
32034 this.renderProgressDialog();
32038 window.addEventListener("resize", function() { _this.refresh(); } );
32040 this.fireEvent('initial', this);
32043 renderProgressDialog : function()
32047 this.progressDialog = new Roo.bootstrap.Modal({
32048 cls : 'roo-document-manager-progress-dialog',
32049 allow_close : false,
32060 btnclick : function() {
32061 _this.uploadCancel();
32067 this.progressDialog.render(Roo.get(document.body));
32069 this.progress = new Roo.bootstrap.Progress({
32070 cls : 'roo-document-manager-progress',
32075 this.progress.render(this.progressDialog.getChildContainer());
32077 this.progressBar = new Roo.bootstrap.ProgressBar({
32078 cls : 'roo-document-manager-progress-bar',
32081 aria_valuemax : 12,
32085 this.progressBar.render(this.progress.getChildContainer());
32088 onUploaderClick : function(e)
32090 e.preventDefault();
32092 if(this.fireEvent('beforeselectfile', this) != false){
32093 this.selectorEl.dom.click();
32098 onFileSelected : function(e)
32100 e.preventDefault();
32102 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32106 Roo.each(this.selectorEl.dom.files, function(file){
32107 if(this.fireEvent('inspect', this, file) != false){
32108 this.files.push(file);
32118 this.selectorEl.dom.value = '';
32120 if(!this.files || !this.files.length){
32124 if(this.boxes > 0 && this.files.length > this.boxes){
32125 this.files = this.files.slice(0, this.boxes);
32128 this.uploader.show();
32130 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32131 this.uploader.hide();
32140 Roo.each(this.files, function(file){
32142 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32143 var f = this.renderPreview(file);
32148 if(file.type.indexOf('image') != -1){
32149 this.delegates.push(
32151 _this.process(file);
32152 }).createDelegate(this)
32160 _this.process(file);
32161 }).createDelegate(this)
32166 this.files = files;
32168 this.delegates = this.delegates.concat(docs);
32170 if(!this.delegates.length){
32175 this.progressBar.aria_valuemax = this.delegates.length;
32182 arrange : function()
32184 if(!this.delegates.length){
32185 this.progressDialog.hide();
32190 var delegate = this.delegates.shift();
32192 this.progressDialog.show();
32194 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32196 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32201 refresh : function()
32203 this.uploader.show();
32205 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32206 this.uploader.hide();
32209 Roo.isTouch ? this.closable(false) : this.closable(true);
32211 this.fireEvent('refresh', this);
32214 onRemove : function(e, el, o)
32216 e.preventDefault();
32218 this.fireEvent('remove', this, o);
32222 remove : function(o)
32226 Roo.each(this.files, function(file){
32227 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32236 this.files = files;
32243 Roo.each(this.files, function(file){
32248 file.target.remove();
32257 onClick : function(e, el, o)
32259 e.preventDefault();
32261 this.fireEvent('click', this, o);
32265 closable : function(closable)
32267 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32269 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32281 xhrOnLoad : function(xhr)
32283 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32287 if (xhr.readyState !== 4) {
32289 this.fireEvent('exception', this, xhr);
32293 var response = Roo.decode(xhr.responseText);
32295 if(!response.success){
32297 this.fireEvent('exception', this, xhr);
32301 var file = this.renderPreview(response.data);
32303 this.files.push(file);
32307 this.fireEvent('afterupload', this, xhr);
32311 xhrOnError : function(xhr)
32313 Roo.log('xhr on error');
32315 var response = Roo.decode(xhr.responseText);
32322 process : function(file)
32324 if(this.fireEvent('process', this, file) !== false){
32325 if(this.editable && file.type.indexOf('image') != -1){
32326 this.fireEvent('edit', this, file);
32330 this.uploadStart(file, false);
32337 uploadStart : function(file, crop)
32339 this.xhr = new XMLHttpRequest();
32341 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32346 file.xhr = this.xhr;
32348 this.managerEl.createChild({
32350 cls : 'roo-document-manager-loading',
32354 tooltip : file.name,
32355 cls : 'roo-document-manager-thumb',
32356 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32362 this.xhr.open(this.method, this.url, true);
32365 "Accept": "application/json",
32366 "Cache-Control": "no-cache",
32367 "X-Requested-With": "XMLHttpRequest"
32370 for (var headerName in headers) {
32371 var headerValue = headers[headerName];
32373 this.xhr.setRequestHeader(headerName, headerValue);
32379 this.xhr.onload = function()
32381 _this.xhrOnLoad(_this.xhr);
32384 this.xhr.onerror = function()
32386 _this.xhrOnError(_this.xhr);
32389 var formData = new FormData();
32391 formData.append('returnHTML', 'NO');
32394 formData.append('crop', crop);
32397 formData.append(this.paramName, file, file.name);
32404 if(this.fireEvent('prepare', this, formData, options) != false){
32406 if(options.manually){
32410 this.xhr.send(formData);
32414 this.uploadCancel();
32417 uploadCancel : function()
32423 this.delegates = [];
32425 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32432 renderPreview : function(file)
32434 if(typeof(file.target) != 'undefined' && file.target){
32438 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32440 var previewEl = this.managerEl.createChild({
32442 cls : 'roo-document-manager-preview',
32446 tooltip : file[this.toolTipName],
32447 cls : 'roo-document-manager-thumb',
32448 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32453 html : '<i class="fa fa-times-circle"></i>'
32458 var close = previewEl.select('button.close', true).first();
32460 close.on('click', this.onRemove, this, file);
32462 file.target = previewEl;
32464 var image = previewEl.select('img', true).first();
32468 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32470 image.on('click', this.onClick, this, file);
32472 this.fireEvent('previewrendered', this, file);
32478 onPreviewLoad : function(file, image)
32480 if(typeof(file.target) == 'undefined' || !file.target){
32484 var width = image.dom.naturalWidth || image.dom.width;
32485 var height = image.dom.naturalHeight || image.dom.height;
32487 if(!this.previewResize) {
32491 if(width > height){
32492 file.target.addClass('wide');
32496 file.target.addClass('tall');
32501 uploadFromSource : function(file, crop)
32503 this.xhr = new XMLHttpRequest();
32505 this.managerEl.createChild({
32507 cls : 'roo-document-manager-loading',
32511 tooltip : file.name,
32512 cls : 'roo-document-manager-thumb',
32513 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32519 this.xhr.open(this.method, this.url, true);
32522 "Accept": "application/json",
32523 "Cache-Control": "no-cache",
32524 "X-Requested-With": "XMLHttpRequest"
32527 for (var headerName in headers) {
32528 var headerValue = headers[headerName];
32530 this.xhr.setRequestHeader(headerName, headerValue);
32536 this.xhr.onload = function()
32538 _this.xhrOnLoad(_this.xhr);
32541 this.xhr.onerror = function()
32543 _this.xhrOnError(_this.xhr);
32546 var formData = new FormData();
32548 formData.append('returnHTML', 'NO');
32550 formData.append('crop', crop);
32552 if(typeof(file.filename) != 'undefined'){
32553 formData.append('filename', file.filename);
32556 if(typeof(file.mimetype) != 'undefined'){
32557 formData.append('mimetype', file.mimetype);
32562 if(this.fireEvent('prepare', this, formData) != false){
32563 this.xhr.send(formData);
32573 * @class Roo.bootstrap.DocumentViewer
32574 * @extends Roo.bootstrap.Component
32575 * Bootstrap DocumentViewer class
32576 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32577 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32580 * Create a new DocumentViewer
32581 * @param {Object} config The config object
32584 Roo.bootstrap.DocumentViewer = function(config){
32585 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32590 * Fire after initEvent
32591 * @param {Roo.bootstrap.DocumentViewer} this
32597 * @param {Roo.bootstrap.DocumentViewer} this
32602 * Fire after download button
32603 * @param {Roo.bootstrap.DocumentViewer} this
32608 * Fire after trash button
32609 * @param {Roo.bootstrap.DocumentViewer} this
32616 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32618 showDownload : true,
32622 getAutoCreate : function()
32626 cls : 'roo-document-viewer',
32630 cls : 'roo-document-viewer-body',
32634 cls : 'roo-document-viewer-thumb',
32638 cls : 'roo-document-viewer-image'
32646 cls : 'roo-document-viewer-footer',
32649 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32653 cls : 'btn-group roo-document-viewer-download',
32657 cls : 'btn btn-default',
32658 html : '<i class="fa fa-download"></i>'
32664 cls : 'btn-group roo-document-viewer-trash',
32668 cls : 'btn btn-default',
32669 html : '<i class="fa fa-trash"></i>'
32682 initEvents : function()
32684 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32685 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32687 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32688 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32690 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32691 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32693 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32694 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32696 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32697 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32699 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32700 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32702 this.bodyEl.on('click', this.onClick, this);
32703 this.downloadBtn.on('click', this.onDownload, this);
32704 this.trashBtn.on('click', this.onTrash, this);
32706 this.downloadBtn.hide();
32707 this.trashBtn.hide();
32709 if(this.showDownload){
32710 this.downloadBtn.show();
32713 if(this.showTrash){
32714 this.trashBtn.show();
32717 if(!this.showDownload && !this.showTrash) {
32718 this.footerEl.hide();
32723 initial : function()
32725 this.fireEvent('initial', this);
32729 onClick : function(e)
32731 e.preventDefault();
32733 this.fireEvent('click', this);
32736 onDownload : function(e)
32738 e.preventDefault();
32740 this.fireEvent('download', this);
32743 onTrash : function(e)
32745 e.preventDefault();
32747 this.fireEvent('trash', this);
32759 * @class Roo.bootstrap.NavProgressBar
32760 * @extends Roo.bootstrap.Component
32761 * Bootstrap NavProgressBar class
32764 * Create a new nav progress bar
32765 * @param {Object} config The config object
32768 Roo.bootstrap.NavProgressBar = function(config){
32769 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32771 this.bullets = this.bullets || [];
32773 // Roo.bootstrap.NavProgressBar.register(this);
32777 * Fires when the active item changes
32778 * @param {Roo.bootstrap.NavProgressBar} this
32779 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32780 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32787 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32792 getAutoCreate : function()
32794 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32798 cls : 'roo-navigation-bar-group',
32802 cls : 'roo-navigation-top-bar'
32806 cls : 'roo-navigation-bullets-bar',
32810 cls : 'roo-navigation-bar'
32817 cls : 'roo-navigation-bottom-bar'
32827 initEvents: function()
32832 onRender : function(ct, position)
32834 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32836 if(this.bullets.length){
32837 Roo.each(this.bullets, function(b){
32846 addItem : function(cfg)
32848 var item = new Roo.bootstrap.NavProgressItem(cfg);
32850 item.parentId = this.id;
32851 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32854 var top = new Roo.bootstrap.Element({
32856 cls : 'roo-navigation-bar-text'
32859 var bottom = new Roo.bootstrap.Element({
32861 cls : 'roo-navigation-bar-text'
32864 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32865 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32867 var topText = new Roo.bootstrap.Element({
32869 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32872 var bottomText = new Roo.bootstrap.Element({
32874 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32877 topText.onRender(top.el, null);
32878 bottomText.onRender(bottom.el, null);
32881 item.bottomEl = bottom;
32884 this.barItems.push(item);
32889 getActive : function()
32891 var active = false;
32893 Roo.each(this.barItems, function(v){
32895 if (!v.isActive()) {
32907 setActiveItem : function(item)
32911 Roo.each(this.barItems, function(v){
32912 if (v.rid == item.rid) {
32916 if (v.isActive()) {
32917 v.setActive(false);
32922 item.setActive(true);
32924 this.fireEvent('changed', this, item, prev);
32927 getBarItem: function(rid)
32931 Roo.each(this.barItems, function(e) {
32932 if (e.rid != rid) {
32943 indexOfItem : function(item)
32947 Roo.each(this.barItems, function(v, i){
32949 if (v.rid != item.rid) {
32960 setActiveNext : function()
32962 var i = this.indexOfItem(this.getActive());
32964 if (i > this.barItems.length) {
32968 this.setActiveItem(this.barItems[i+1]);
32971 setActivePrev : function()
32973 var i = this.indexOfItem(this.getActive());
32979 this.setActiveItem(this.barItems[i-1]);
32982 format : function()
32984 if(!this.barItems.length){
32988 var width = 100 / this.barItems.length;
32990 Roo.each(this.barItems, function(i){
32991 i.el.setStyle('width', width + '%');
32992 i.topEl.el.setStyle('width', width + '%');
32993 i.bottomEl.el.setStyle('width', width + '%');
33002 * Nav Progress Item
33007 * @class Roo.bootstrap.NavProgressItem
33008 * @extends Roo.bootstrap.Component
33009 * Bootstrap NavProgressItem class
33010 * @cfg {String} rid the reference id
33011 * @cfg {Boolean} active (true|false) Is item active default false
33012 * @cfg {Boolean} disabled (true|false) Is item active default false
33013 * @cfg {String} html
33014 * @cfg {String} position (top|bottom) text position default bottom
33015 * @cfg {String} icon show icon instead of number
33018 * Create a new NavProgressItem
33019 * @param {Object} config The config object
33021 Roo.bootstrap.NavProgressItem = function(config){
33022 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33027 * The raw click event for the entire grid.
33028 * @param {Roo.bootstrap.NavProgressItem} this
33029 * @param {Roo.EventObject} e
33036 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33042 position : 'bottom',
33045 getAutoCreate : function()
33047 var iconCls = 'roo-navigation-bar-item-icon';
33049 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33053 cls: 'roo-navigation-bar-item',
33063 cfg.cls += ' active';
33066 cfg.cls += ' disabled';
33072 disable : function()
33074 this.setDisabled(true);
33077 enable : function()
33079 this.setDisabled(false);
33082 initEvents: function()
33084 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33086 this.iconEl.on('click', this.onClick, this);
33089 onClick : function(e)
33091 e.preventDefault();
33097 if(this.fireEvent('click', this, e) === false){
33101 this.parent().setActiveItem(this);
33104 isActive: function ()
33106 return this.active;
33109 setActive : function(state)
33111 if(this.active == state){
33115 this.active = state;
33118 this.el.addClass('active');
33122 this.el.removeClass('active');
33127 setDisabled : function(state)
33129 if(this.disabled == state){
33133 this.disabled = state;
33136 this.el.addClass('disabled');
33140 this.el.removeClass('disabled');
33143 tooltipEl : function()
33145 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33158 * @class Roo.bootstrap.FieldLabel
33159 * @extends Roo.bootstrap.Component
33160 * Bootstrap FieldLabel class
33161 * @cfg {String} html contents of the element
33162 * @cfg {String} tag tag of the element default label
33163 * @cfg {String} cls class of the element
33164 * @cfg {String} target label target
33165 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33166 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33167 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33168 * @cfg {String} iconTooltip default "This field is required"
33169 * @cfg {String} indicatorpos (left|right) default left
33172 * Create a new FieldLabel
33173 * @param {Object} config The config object
33176 Roo.bootstrap.FieldLabel = function(config){
33177 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33182 * Fires after the field has been marked as invalid.
33183 * @param {Roo.form.FieldLabel} this
33184 * @param {String} msg The validation message
33189 * Fires after the field has been validated with no errors.
33190 * @param {Roo.form.FieldLabel} this
33196 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33203 invalidClass : 'has-warning',
33204 validClass : 'has-success',
33205 iconTooltip : 'This field is required',
33206 indicatorpos : 'left',
33208 getAutoCreate : function(){
33211 if (!this.allowBlank) {
33217 cls : 'roo-bootstrap-field-label ' + this.cls,
33222 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33223 tooltip : this.iconTooltip
33232 if(this.indicatorpos == 'right'){
33235 cls : 'roo-bootstrap-field-label ' + this.cls,
33244 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33245 tooltip : this.iconTooltip
33254 initEvents: function()
33256 Roo.bootstrap.Element.superclass.initEvents.call(this);
33258 this.indicator = this.indicatorEl();
33260 if(this.indicator){
33261 this.indicator.removeClass('visible');
33262 this.indicator.addClass('invisible');
33265 Roo.bootstrap.FieldLabel.register(this);
33268 indicatorEl : function()
33270 var indicator = this.el.select('i.roo-required-indicator',true).first();
33281 * Mark this field as valid
33283 markValid : function()
33285 if(this.indicator){
33286 this.indicator.removeClass('visible');
33287 this.indicator.addClass('invisible');
33289 if (Roo.bootstrap.version == 3) {
33290 this.el.removeClass(this.invalidClass);
33291 this.el.addClass(this.validClass);
33293 this.el.removeClass('is-invalid');
33294 this.el.addClass('is-valid');
33298 this.fireEvent('valid', this);
33302 * Mark this field as invalid
33303 * @param {String} msg The validation message
33305 markInvalid : function(msg)
33307 if(this.indicator){
33308 this.indicator.removeClass('invisible');
33309 this.indicator.addClass('visible');
33311 if (Roo.bootstrap.version == 3) {
33312 this.el.removeClass(this.validClass);
33313 this.el.addClass(this.invalidClass);
33315 this.el.removeClass('is-valid');
33316 this.el.addClass('is-invalid');
33320 this.fireEvent('invalid', this, msg);
33326 Roo.apply(Roo.bootstrap.FieldLabel, {
33331 * register a FieldLabel Group
33332 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33334 register : function(label)
33336 if(this.groups.hasOwnProperty(label.target)){
33340 this.groups[label.target] = label;
33344 * fetch a FieldLabel Group based on the target
33345 * @param {string} target
33346 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33348 get: function(target) {
33349 if (typeof(this.groups[target]) == 'undefined') {
33353 return this.groups[target] ;
33362 * page DateSplitField.
33368 * @class Roo.bootstrap.DateSplitField
33369 * @extends Roo.bootstrap.Component
33370 * Bootstrap DateSplitField class
33371 * @cfg {string} fieldLabel - the label associated
33372 * @cfg {Number} labelWidth set the width of label (0-12)
33373 * @cfg {String} labelAlign (top|left)
33374 * @cfg {Boolean} dayAllowBlank (true|false) default false
33375 * @cfg {Boolean} monthAllowBlank (true|false) default false
33376 * @cfg {Boolean} yearAllowBlank (true|false) default false
33377 * @cfg {string} dayPlaceholder
33378 * @cfg {string} monthPlaceholder
33379 * @cfg {string} yearPlaceholder
33380 * @cfg {string} dayFormat default 'd'
33381 * @cfg {string} monthFormat default 'm'
33382 * @cfg {string} yearFormat default 'Y'
33383 * @cfg {Number} labellg set the width of label (1-12)
33384 * @cfg {Number} labelmd set the width of label (1-12)
33385 * @cfg {Number} labelsm set the width of label (1-12)
33386 * @cfg {Number} labelxs set the width of label (1-12)
33390 * Create a new DateSplitField
33391 * @param {Object} config The config object
33394 Roo.bootstrap.DateSplitField = function(config){
33395 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33401 * getting the data of years
33402 * @param {Roo.bootstrap.DateSplitField} this
33403 * @param {Object} years
33408 * getting the data of days
33409 * @param {Roo.bootstrap.DateSplitField} this
33410 * @param {Object} days
33415 * Fires after the field has been marked as invalid.
33416 * @param {Roo.form.Field} this
33417 * @param {String} msg The validation message
33422 * Fires after the field has been validated with no errors.
33423 * @param {Roo.form.Field} this
33429 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33432 labelAlign : 'top',
33434 dayAllowBlank : false,
33435 monthAllowBlank : false,
33436 yearAllowBlank : false,
33437 dayPlaceholder : '',
33438 monthPlaceholder : '',
33439 yearPlaceholder : '',
33443 isFormField : true,
33449 getAutoCreate : function()
33453 cls : 'row roo-date-split-field-group',
33458 cls : 'form-hidden-field roo-date-split-field-group-value',
33464 var labelCls = 'col-md-12';
33465 var contentCls = 'col-md-4';
33467 if(this.fieldLabel){
33471 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33475 html : this.fieldLabel
33480 if(this.labelAlign == 'left'){
33482 if(this.labelWidth > 12){
33483 label.style = "width: " + this.labelWidth + 'px';
33486 if(this.labelWidth < 13 && this.labelmd == 0){
33487 this.labelmd = this.labelWidth;
33490 if(this.labellg > 0){
33491 labelCls = ' col-lg-' + this.labellg;
33492 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33495 if(this.labelmd > 0){
33496 labelCls = ' col-md-' + this.labelmd;
33497 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33500 if(this.labelsm > 0){
33501 labelCls = ' col-sm-' + this.labelsm;
33502 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33505 if(this.labelxs > 0){
33506 labelCls = ' col-xs-' + this.labelxs;
33507 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33511 label.cls += ' ' + labelCls;
33513 cfg.cn.push(label);
33516 Roo.each(['day', 'month', 'year'], function(t){
33519 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33526 inputEl: function ()
33528 return this.el.select('.roo-date-split-field-group-value', true).first();
33531 onRender : function(ct, position)
33535 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33537 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33539 this.dayField = new Roo.bootstrap.ComboBox({
33540 allowBlank : this.dayAllowBlank,
33541 alwaysQuery : true,
33542 displayField : 'value',
33545 forceSelection : true,
33547 placeholder : this.dayPlaceholder,
33548 selectOnFocus : true,
33549 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33550 triggerAction : 'all',
33552 valueField : 'value',
33553 store : new Roo.data.SimpleStore({
33554 data : (function() {
33556 _this.fireEvent('days', _this, days);
33559 fields : [ 'value' ]
33562 select : function (_self, record, index)
33564 _this.setValue(_this.getValue());
33569 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33571 this.monthField = new Roo.bootstrap.MonthField({
33572 after : '<i class=\"fa fa-calendar\"></i>',
33573 allowBlank : this.monthAllowBlank,
33574 placeholder : this.monthPlaceholder,
33577 render : function (_self)
33579 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33580 e.preventDefault();
33584 select : function (_self, oldvalue, newvalue)
33586 _this.setValue(_this.getValue());
33591 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33593 this.yearField = new Roo.bootstrap.ComboBox({
33594 allowBlank : this.yearAllowBlank,
33595 alwaysQuery : true,
33596 displayField : 'value',
33599 forceSelection : true,
33601 placeholder : this.yearPlaceholder,
33602 selectOnFocus : true,
33603 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33604 triggerAction : 'all',
33606 valueField : 'value',
33607 store : new Roo.data.SimpleStore({
33608 data : (function() {
33610 _this.fireEvent('years', _this, years);
33613 fields : [ 'value' ]
33616 select : function (_self, record, index)
33618 _this.setValue(_this.getValue());
33623 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33626 setValue : function(v, format)
33628 this.inputEl.dom.value = v;
33630 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33632 var d = Date.parseDate(v, f);
33639 this.setDay(d.format(this.dayFormat));
33640 this.setMonth(d.format(this.monthFormat));
33641 this.setYear(d.format(this.yearFormat));
33648 setDay : function(v)
33650 this.dayField.setValue(v);
33651 this.inputEl.dom.value = this.getValue();
33656 setMonth : function(v)
33658 this.monthField.setValue(v, true);
33659 this.inputEl.dom.value = this.getValue();
33664 setYear : function(v)
33666 this.yearField.setValue(v);
33667 this.inputEl.dom.value = this.getValue();
33672 getDay : function()
33674 return this.dayField.getValue();
33677 getMonth : function()
33679 return this.monthField.getValue();
33682 getYear : function()
33684 return this.yearField.getValue();
33687 getValue : function()
33689 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33691 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33701 this.inputEl.dom.value = '';
33706 validate : function()
33708 var d = this.dayField.validate();
33709 var m = this.monthField.validate();
33710 var y = this.yearField.validate();
33715 (!this.dayAllowBlank && !d) ||
33716 (!this.monthAllowBlank && !m) ||
33717 (!this.yearAllowBlank && !y)
33722 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33731 this.markInvalid();
33736 markValid : function()
33739 var label = this.el.select('label', true).first();
33740 var icon = this.el.select('i.fa-star', true).first();
33746 this.fireEvent('valid', this);
33750 * Mark this field as invalid
33751 * @param {String} msg The validation message
33753 markInvalid : function(msg)
33756 var label = this.el.select('label', true).first();
33757 var icon = this.el.select('i.fa-star', true).first();
33759 if(label && !icon){
33760 this.el.select('.roo-date-split-field-label', true).createChild({
33762 cls : 'text-danger fa fa-lg fa-star',
33763 tooltip : 'This field is required',
33764 style : 'margin-right:5px;'
33768 this.fireEvent('invalid', this, msg);
33771 clearInvalid : function()
33773 var label = this.el.select('label', true).first();
33774 var icon = this.el.select('i.fa-star', true).first();
33780 this.fireEvent('valid', this);
33783 getName: function()
33793 * http://masonry.desandro.com
33795 * The idea is to render all the bricks based on vertical width...
33797 * The original code extends 'outlayer' - we might need to use that....
33803 * @class Roo.bootstrap.LayoutMasonry
33804 * @extends Roo.bootstrap.Component
33805 * Bootstrap Layout Masonry class
33808 * Create a new Element
33809 * @param {Object} config The config object
33812 Roo.bootstrap.LayoutMasonry = function(config){
33814 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33818 Roo.bootstrap.LayoutMasonry.register(this);
33824 * Fire after layout the items
33825 * @param {Roo.bootstrap.LayoutMasonry} this
33826 * @param {Roo.EventObject} e
33833 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33836 * @cfg {Boolean} isLayoutInstant = no animation?
33838 isLayoutInstant : false, // needed?
33841 * @cfg {Number} boxWidth width of the columns
33846 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33851 * @cfg {Number} padWidth padding below box..
33856 * @cfg {Number} gutter gutter width..
33861 * @cfg {Number} maxCols maximum number of columns
33867 * @cfg {Boolean} isAutoInitial defalut true
33869 isAutoInitial : true,
33874 * @cfg {Boolean} isHorizontal defalut false
33876 isHorizontal : false,
33878 currentSize : null,
33884 bricks: null, //CompositeElement
33888 _isLayoutInited : false,
33890 // isAlternative : false, // only use for vertical layout...
33893 * @cfg {Number} alternativePadWidth padding below box..
33895 alternativePadWidth : 50,
33897 selectedBrick : [],
33899 getAutoCreate : function(){
33901 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33905 cls: 'blog-masonary-wrapper ' + this.cls,
33907 cls : 'mas-boxes masonary'
33914 getChildContainer: function( )
33916 if (this.boxesEl) {
33917 return this.boxesEl;
33920 this.boxesEl = this.el.select('.mas-boxes').first();
33922 return this.boxesEl;
33926 initEvents : function()
33930 if(this.isAutoInitial){
33931 Roo.log('hook children rendered');
33932 this.on('childrenrendered', function() {
33933 Roo.log('children rendered');
33939 initial : function()
33941 this.selectedBrick = [];
33943 this.currentSize = this.el.getBox(true);
33945 Roo.EventManager.onWindowResize(this.resize, this);
33947 if(!this.isAutoInitial){
33955 //this.layout.defer(500,this);
33959 resize : function()
33961 var cs = this.el.getBox(true);
33964 this.currentSize.width == cs.width &&
33965 this.currentSize.x == cs.x &&
33966 this.currentSize.height == cs.height &&
33967 this.currentSize.y == cs.y
33969 Roo.log("no change in with or X or Y");
33973 this.currentSize = cs;
33979 layout : function()
33981 this._resetLayout();
33983 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33985 this.layoutItems( isInstant );
33987 this._isLayoutInited = true;
33989 this.fireEvent('layout', this);
33993 _resetLayout : function()
33995 if(this.isHorizontal){
33996 this.horizontalMeasureColumns();
34000 this.verticalMeasureColumns();
34004 verticalMeasureColumns : function()
34006 this.getContainerWidth();
34008 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34009 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34013 var boxWidth = this.boxWidth + this.padWidth;
34015 if(this.containerWidth < this.boxWidth){
34016 boxWidth = this.containerWidth
34019 var containerWidth = this.containerWidth;
34021 var cols = Math.floor(containerWidth / boxWidth);
34023 this.cols = Math.max( cols, 1 );
34025 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34027 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34029 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34031 this.colWidth = boxWidth + avail - this.padWidth;
34033 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34034 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34037 horizontalMeasureColumns : function()
34039 this.getContainerWidth();
34041 var boxWidth = this.boxWidth;
34043 if(this.containerWidth < boxWidth){
34044 boxWidth = this.containerWidth;
34047 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34049 this.el.setHeight(boxWidth);
34053 getContainerWidth : function()
34055 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34058 layoutItems : function( isInstant )
34060 Roo.log(this.bricks);
34062 var items = Roo.apply([], this.bricks);
34064 if(this.isHorizontal){
34065 this._horizontalLayoutItems( items , isInstant );
34069 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34070 // this._verticalAlternativeLayoutItems( items , isInstant );
34074 this._verticalLayoutItems( items , isInstant );
34078 _verticalLayoutItems : function ( items , isInstant)
34080 if ( !items || !items.length ) {
34085 ['xs', 'xs', 'xs', 'tall'],
34086 ['xs', 'xs', 'tall'],
34087 ['xs', 'xs', 'sm'],
34088 ['xs', 'xs', 'xs'],
34094 ['sm', 'xs', 'xs'],
34098 ['tall', 'xs', 'xs', 'xs'],
34099 ['tall', 'xs', 'xs'],
34111 Roo.each(items, function(item, k){
34113 switch (item.size) {
34114 // these layouts take up a full box,
34125 boxes.push([item]);
34148 var filterPattern = function(box, length)
34156 var pattern = box.slice(0, length);
34160 Roo.each(pattern, function(i){
34161 format.push(i.size);
34164 Roo.each(standard, function(s){
34166 if(String(s) != String(format)){
34175 if(!match && length == 1){
34180 filterPattern(box, length - 1);
34184 queue.push(pattern);
34186 box = box.slice(length, box.length);
34188 filterPattern(box, 4);
34194 Roo.each(boxes, function(box, k){
34200 if(box.length == 1){
34205 filterPattern(box, 4);
34209 this._processVerticalLayoutQueue( queue, isInstant );
34213 // _verticalAlternativeLayoutItems : function( items , isInstant )
34215 // if ( !items || !items.length ) {
34219 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34223 _horizontalLayoutItems : function ( items , isInstant)
34225 if ( !items || !items.length || items.length < 3) {
34231 var eItems = items.slice(0, 3);
34233 items = items.slice(3, items.length);
34236 ['xs', 'xs', 'xs', 'wide'],
34237 ['xs', 'xs', 'wide'],
34238 ['xs', 'xs', 'sm'],
34239 ['xs', 'xs', 'xs'],
34245 ['sm', 'xs', 'xs'],
34249 ['wide', 'xs', 'xs', 'xs'],
34250 ['wide', 'xs', 'xs'],
34263 Roo.each(items, function(item, k){
34265 switch (item.size) {
34276 boxes.push([item]);
34300 var filterPattern = function(box, length)
34308 var pattern = box.slice(0, length);
34312 Roo.each(pattern, function(i){
34313 format.push(i.size);
34316 Roo.each(standard, function(s){
34318 if(String(s) != String(format)){
34327 if(!match && length == 1){
34332 filterPattern(box, length - 1);
34336 queue.push(pattern);
34338 box = box.slice(length, box.length);
34340 filterPattern(box, 4);
34346 Roo.each(boxes, function(box, k){
34352 if(box.length == 1){
34357 filterPattern(box, 4);
34364 var pos = this.el.getBox(true);
34368 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34370 var hit_end = false;
34372 Roo.each(queue, function(box){
34376 Roo.each(box, function(b){
34378 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34388 Roo.each(box, function(b){
34390 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34393 mx = Math.max(mx, b.x);
34397 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34401 Roo.each(box, function(b){
34403 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34417 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34420 /** Sets position of item in DOM
34421 * @param {Element} item
34422 * @param {Number} x - horizontal position
34423 * @param {Number} y - vertical position
34424 * @param {Boolean} isInstant - disables transitions
34426 _processVerticalLayoutQueue : function( queue, isInstant )
34428 var pos = this.el.getBox(true);
34433 for (var i = 0; i < this.cols; i++){
34437 Roo.each(queue, function(box, k){
34439 var col = k % this.cols;
34441 Roo.each(box, function(b,kk){
34443 b.el.position('absolute');
34445 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34446 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34448 if(b.size == 'md-left' || b.size == 'md-right'){
34449 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34450 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34453 b.el.setWidth(width);
34454 b.el.setHeight(height);
34456 b.el.select('iframe',true).setSize(width,height);
34460 for (var i = 0; i < this.cols; i++){
34462 if(maxY[i] < maxY[col]){
34467 col = Math.min(col, i);
34471 x = pos.x + col * (this.colWidth + this.padWidth);
34475 var positions = [];
34477 switch (box.length){
34479 positions = this.getVerticalOneBoxColPositions(x, y, box);
34482 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34485 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34488 positions = this.getVerticalFourBoxColPositions(x, y, box);
34494 Roo.each(box, function(b,kk){
34496 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34498 var sz = b.el.getSize();
34500 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34508 for (var i = 0; i < this.cols; i++){
34509 mY = Math.max(mY, maxY[i]);
34512 this.el.setHeight(mY - pos.y);
34516 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34518 // var pos = this.el.getBox(true);
34521 // var maxX = pos.right;
34523 // var maxHeight = 0;
34525 // Roo.each(items, function(item, k){
34529 // item.el.position('absolute');
34531 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34533 // item.el.setWidth(width);
34535 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34537 // item.el.setHeight(height);
34540 // item.el.setXY([x, y], isInstant ? false : true);
34542 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34545 // y = y + height + this.alternativePadWidth;
34547 // maxHeight = maxHeight + height + this.alternativePadWidth;
34551 // this.el.setHeight(maxHeight);
34555 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34557 var pos = this.el.getBox(true);
34562 var maxX = pos.right;
34564 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34566 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34568 Roo.each(queue, function(box, k){
34570 Roo.each(box, function(b, kk){
34572 b.el.position('absolute');
34574 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34575 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34577 if(b.size == 'md-left' || b.size == 'md-right'){
34578 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34579 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34582 b.el.setWidth(width);
34583 b.el.setHeight(height);
34591 var positions = [];
34593 switch (box.length){
34595 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34598 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34601 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34604 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34610 Roo.each(box, function(b,kk){
34612 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34614 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34622 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34624 Roo.each(eItems, function(b,k){
34626 b.size = (k == 0) ? 'sm' : 'xs';
34627 b.x = (k == 0) ? 2 : 1;
34628 b.y = (k == 0) ? 2 : 1;
34630 b.el.position('absolute');
34632 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34634 b.el.setWidth(width);
34636 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34638 b.el.setHeight(height);
34642 var positions = [];
34645 x : maxX - this.unitWidth * 2 - this.gutter,
34650 x : maxX - this.unitWidth,
34651 y : minY + (this.unitWidth + this.gutter) * 2
34655 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34659 Roo.each(eItems, function(b,k){
34661 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34667 getVerticalOneBoxColPositions : function(x, y, box)
34671 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34673 if(box[0].size == 'md-left'){
34677 if(box[0].size == 'md-right'){
34682 x : x + (this.unitWidth + this.gutter) * rand,
34689 getVerticalTwoBoxColPositions : function(x, y, box)
34693 if(box[0].size == 'xs'){
34697 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34701 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34715 x : x + (this.unitWidth + this.gutter) * 2,
34716 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34723 getVerticalThreeBoxColPositions : function(x, y, box)
34727 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34735 x : x + (this.unitWidth + this.gutter) * 1,
34740 x : x + (this.unitWidth + this.gutter) * 2,
34748 if(box[0].size == 'xs' && box[1].size == 'xs'){
34757 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34761 x : x + (this.unitWidth + this.gutter) * 1,
34775 x : x + (this.unitWidth + this.gutter) * 2,
34780 x : x + (this.unitWidth + this.gutter) * 2,
34781 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34788 getVerticalFourBoxColPositions : function(x, y, box)
34792 if(box[0].size == 'xs'){
34801 y : y + (this.unitHeight + this.gutter) * 1
34806 y : y + (this.unitHeight + this.gutter) * 2
34810 x : x + (this.unitWidth + this.gutter) * 1,
34824 x : x + (this.unitWidth + this.gutter) * 2,
34829 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34830 y : y + (this.unitHeight + this.gutter) * 1
34834 x : x + (this.unitWidth + this.gutter) * 2,
34835 y : y + (this.unitWidth + this.gutter) * 2
34842 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34846 if(box[0].size == 'md-left'){
34848 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34855 if(box[0].size == 'md-right'){
34857 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34858 y : minY + (this.unitWidth + this.gutter) * 1
34864 var rand = Math.floor(Math.random() * (4 - box[0].y));
34867 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34868 y : minY + (this.unitWidth + this.gutter) * rand
34875 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34879 if(box[0].size == 'xs'){
34882 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34887 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34888 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34896 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34901 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34902 y : minY + (this.unitWidth + this.gutter) * 2
34909 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34913 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34916 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34921 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34922 y : minY + (this.unitWidth + this.gutter) * 1
34926 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34927 y : minY + (this.unitWidth + this.gutter) * 2
34934 if(box[0].size == 'xs' && box[1].size == 'xs'){
34937 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34942 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34947 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34948 y : minY + (this.unitWidth + this.gutter) * 1
34956 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34961 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34962 y : minY + (this.unitWidth + this.gutter) * 2
34966 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34967 y : minY + (this.unitWidth + this.gutter) * 2
34974 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34978 if(box[0].size == 'xs'){
34981 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34986 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34991 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),
34996 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34997 y : minY + (this.unitWidth + this.gutter) * 1
35005 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35010 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35011 y : minY + (this.unitWidth + this.gutter) * 2
35015 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35016 y : minY + (this.unitWidth + this.gutter) * 2
35020 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),
35021 y : minY + (this.unitWidth + this.gutter) * 2
35029 * remove a Masonry Brick
35030 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35032 removeBrick : function(brick_id)
35038 for (var i = 0; i<this.bricks.length; i++) {
35039 if (this.bricks[i].id == brick_id) {
35040 this.bricks.splice(i,1);
35041 this.el.dom.removeChild(Roo.get(brick_id).dom);
35048 * adds a Masonry Brick
35049 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35051 addBrick : function(cfg)
35053 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35054 //this.register(cn);
35055 cn.parentId = this.id;
35056 cn.render(this.el);
35061 * register a Masonry Brick
35062 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35065 register : function(brick)
35067 this.bricks.push(brick);
35068 brick.masonryId = this.id;
35072 * clear all the Masonry Brick
35074 clearAll : function()
35077 //this.getChildContainer().dom.innerHTML = "";
35078 this.el.dom.innerHTML = '';
35081 getSelected : function()
35083 if (!this.selectedBrick) {
35087 return this.selectedBrick;
35091 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35095 * register a Masonry Layout
35096 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35099 register : function(layout)
35101 this.groups[layout.id] = layout;
35104 * fetch a Masonry Layout based on the masonry layout ID
35105 * @param {string} the masonry layout to add
35106 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35109 get: function(layout_id) {
35110 if (typeof(this.groups[layout_id]) == 'undefined') {
35113 return this.groups[layout_id] ;
35125 * http://masonry.desandro.com
35127 * The idea is to render all the bricks based on vertical width...
35129 * The original code extends 'outlayer' - we might need to use that....
35135 * @class Roo.bootstrap.LayoutMasonryAuto
35136 * @extends Roo.bootstrap.Component
35137 * Bootstrap Layout Masonry class
35140 * Create a new Element
35141 * @param {Object} config The config object
35144 Roo.bootstrap.LayoutMasonryAuto = function(config){
35145 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35148 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35151 * @cfg {Boolean} isFitWidth - resize the width..
35153 isFitWidth : false, // options..
35155 * @cfg {Boolean} isOriginLeft = left align?
35157 isOriginLeft : true,
35159 * @cfg {Boolean} isOriginTop = top align?
35161 isOriginTop : false,
35163 * @cfg {Boolean} isLayoutInstant = no animation?
35165 isLayoutInstant : false, // needed?
35167 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35169 isResizingContainer : true,
35171 * @cfg {Number} columnWidth width of the columns
35177 * @cfg {Number} maxCols maximum number of columns
35182 * @cfg {Number} padHeight padding below box..
35188 * @cfg {Boolean} isAutoInitial defalut true
35191 isAutoInitial : true,
35197 initialColumnWidth : 0,
35198 currentSize : null,
35200 colYs : null, // array.
35207 bricks: null, //CompositeElement
35208 cols : 0, // array?
35209 // element : null, // wrapped now this.el
35210 _isLayoutInited : null,
35213 getAutoCreate : function(){
35217 cls: 'blog-masonary-wrapper ' + this.cls,
35219 cls : 'mas-boxes masonary'
35226 getChildContainer: function( )
35228 if (this.boxesEl) {
35229 return this.boxesEl;
35232 this.boxesEl = this.el.select('.mas-boxes').first();
35234 return this.boxesEl;
35238 initEvents : function()
35242 if(this.isAutoInitial){
35243 Roo.log('hook children rendered');
35244 this.on('childrenrendered', function() {
35245 Roo.log('children rendered');
35252 initial : function()
35254 this.reloadItems();
35256 this.currentSize = this.el.getBox(true);
35258 /// was window resize... - let's see if this works..
35259 Roo.EventManager.onWindowResize(this.resize, this);
35261 if(!this.isAutoInitial){
35266 this.layout.defer(500,this);
35269 reloadItems: function()
35271 this.bricks = this.el.select('.masonry-brick', true);
35273 this.bricks.each(function(b) {
35274 //Roo.log(b.getSize());
35275 if (!b.attr('originalwidth')) {
35276 b.attr('originalwidth', b.getSize().width);
35281 Roo.log(this.bricks.elements.length);
35284 resize : function()
35287 var cs = this.el.getBox(true);
35289 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35290 Roo.log("no change in with or X");
35293 this.currentSize = cs;
35297 layout : function()
35300 this._resetLayout();
35301 //this._manageStamps();
35303 // don't animate first layout
35304 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35305 this.layoutItems( isInstant );
35307 // flag for initalized
35308 this._isLayoutInited = true;
35311 layoutItems : function( isInstant )
35313 //var items = this._getItemsForLayout( this.items );
35314 // original code supports filtering layout items.. we just ignore it..
35316 this._layoutItems( this.bricks , isInstant );
35318 this._postLayout();
35320 _layoutItems : function ( items , isInstant)
35322 //this.fireEvent( 'layout', this, items );
35325 if ( !items || !items.elements.length ) {
35326 // no items, emit event with empty array
35331 items.each(function(item) {
35332 Roo.log("layout item");
35334 // get x/y object from method
35335 var position = this._getItemLayoutPosition( item );
35337 position.item = item;
35338 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35339 queue.push( position );
35342 this._processLayoutQueue( queue );
35344 /** Sets position of item in DOM
35345 * @param {Element} item
35346 * @param {Number} x - horizontal position
35347 * @param {Number} y - vertical position
35348 * @param {Boolean} isInstant - disables transitions
35350 _processLayoutQueue : function( queue )
35352 for ( var i=0, len = queue.length; i < len; i++ ) {
35353 var obj = queue[i];
35354 obj.item.position('absolute');
35355 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35361 * Any logic you want to do after each layout,
35362 * i.e. size the container
35364 _postLayout : function()
35366 this.resizeContainer();
35369 resizeContainer : function()
35371 if ( !this.isResizingContainer ) {
35374 var size = this._getContainerSize();
35376 this.el.setSize(size.width,size.height);
35377 this.boxesEl.setSize(size.width,size.height);
35383 _resetLayout : function()
35385 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35386 this.colWidth = this.el.getWidth();
35387 //this.gutter = this.el.getWidth();
35389 this.measureColumns();
35395 this.colYs.push( 0 );
35401 measureColumns : function()
35403 this.getContainerWidth();
35404 // if columnWidth is 0, default to outerWidth of first item
35405 if ( !this.columnWidth ) {
35406 var firstItem = this.bricks.first();
35407 Roo.log(firstItem);
35408 this.columnWidth = this.containerWidth;
35409 if (firstItem && firstItem.attr('originalwidth') ) {
35410 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35412 // columnWidth fall back to item of first element
35413 Roo.log("set column width?");
35414 this.initialColumnWidth = this.columnWidth ;
35416 // if first elem has no width, default to size of container
35421 if (this.initialColumnWidth) {
35422 this.columnWidth = this.initialColumnWidth;
35427 // column width is fixed at the top - however if container width get's smaller we should
35430 // this bit calcs how man columns..
35432 var columnWidth = this.columnWidth += this.gutter;
35434 // calculate columns
35435 var containerWidth = this.containerWidth + this.gutter;
35437 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35438 // fix rounding errors, typically with gutters
35439 var excess = columnWidth - containerWidth % columnWidth;
35442 // if overshoot is less than a pixel, round up, otherwise floor it
35443 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35444 cols = Math[ mathMethod ]( cols );
35445 this.cols = Math.max( cols, 1 );
35446 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35448 // padding positioning..
35449 var totalColWidth = this.cols * this.columnWidth;
35450 var padavail = this.containerWidth - totalColWidth;
35451 // so for 2 columns - we need 3 'pads'
35453 var padNeeded = (1+this.cols) * this.padWidth;
35455 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35457 this.columnWidth += padExtra
35458 //this.padWidth = Math.floor(padavail / ( this.cols));
35460 // adjust colum width so that padding is fixed??
35462 // we have 3 columns ... total = width * 3
35463 // we have X left over... that should be used by
35465 //if (this.expandC) {
35473 getContainerWidth : function()
35475 /* // container is parent if fit width
35476 var container = this.isFitWidth ? this.element.parentNode : this.element;
35477 // check that this.size and size are there
35478 // IE8 triggers resize on body size change, so they might not be
35480 var size = getSize( container ); //FIXME
35481 this.containerWidth = size && size.innerWidth; //FIXME
35484 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35488 _getItemLayoutPosition : function( item ) // what is item?
35490 // we resize the item to our columnWidth..
35492 item.setWidth(this.columnWidth);
35493 item.autoBoxAdjust = false;
35495 var sz = item.getSize();
35497 // how many columns does this brick span
35498 var remainder = this.containerWidth % this.columnWidth;
35500 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35501 // round if off by 1 pixel, otherwise use ceil
35502 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35503 colSpan = Math.min( colSpan, this.cols );
35505 // normally this should be '1' as we dont' currently allow multi width columns..
35507 var colGroup = this._getColGroup( colSpan );
35508 // get the minimum Y value from the columns
35509 var minimumY = Math.min.apply( Math, colGroup );
35510 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35512 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35514 // position the brick
35516 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35517 y: this.currentSize.y + minimumY + this.padHeight
35521 // apply setHeight to necessary columns
35522 var setHeight = minimumY + sz.height + this.padHeight;
35523 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35525 var setSpan = this.cols + 1 - colGroup.length;
35526 for ( var i = 0; i < setSpan; i++ ) {
35527 this.colYs[ shortColIndex + i ] = setHeight ;
35534 * @param {Number} colSpan - number of columns the element spans
35535 * @returns {Array} colGroup
35537 _getColGroup : function( colSpan )
35539 if ( colSpan < 2 ) {
35540 // if brick spans only one column, use all the column Ys
35545 // how many different places could this brick fit horizontally
35546 var groupCount = this.cols + 1 - colSpan;
35547 // for each group potential horizontal position
35548 for ( var i = 0; i < groupCount; i++ ) {
35549 // make an array of colY values for that one group
35550 var groupColYs = this.colYs.slice( i, i + colSpan );
35551 // and get the max value of the array
35552 colGroup[i] = Math.max.apply( Math, groupColYs );
35557 _manageStamp : function( stamp )
35559 var stampSize = stamp.getSize();
35560 var offset = stamp.getBox();
35561 // get the columns that this stamp affects
35562 var firstX = this.isOriginLeft ? offset.x : offset.right;
35563 var lastX = firstX + stampSize.width;
35564 var firstCol = Math.floor( firstX / this.columnWidth );
35565 firstCol = Math.max( 0, firstCol );
35567 var lastCol = Math.floor( lastX / this.columnWidth );
35568 // lastCol should not go over if multiple of columnWidth #425
35569 lastCol -= lastX % this.columnWidth ? 0 : 1;
35570 lastCol = Math.min( this.cols - 1, lastCol );
35572 // set colYs to bottom of the stamp
35573 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35576 for ( var i = firstCol; i <= lastCol; i++ ) {
35577 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35582 _getContainerSize : function()
35584 this.maxY = Math.max.apply( Math, this.colYs );
35589 if ( this.isFitWidth ) {
35590 size.width = this._getContainerFitWidth();
35596 _getContainerFitWidth : function()
35598 var unusedCols = 0;
35599 // count unused columns
35602 if ( this.colYs[i] !== 0 ) {
35607 // fit container to columns that have been used
35608 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35611 needsResizeLayout : function()
35613 var previousWidth = this.containerWidth;
35614 this.getContainerWidth();
35615 return previousWidth !== this.containerWidth;
35630 * @class Roo.bootstrap.MasonryBrick
35631 * @extends Roo.bootstrap.Component
35632 * Bootstrap MasonryBrick class
35635 * Create a new MasonryBrick
35636 * @param {Object} config The config object
35639 Roo.bootstrap.MasonryBrick = function(config){
35641 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35643 Roo.bootstrap.MasonryBrick.register(this);
35649 * When a MasonryBrick is clcik
35650 * @param {Roo.bootstrap.MasonryBrick} this
35651 * @param {Roo.EventObject} e
35657 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35660 * @cfg {String} title
35664 * @cfg {String} html
35668 * @cfg {String} bgimage
35672 * @cfg {String} videourl
35676 * @cfg {String} cls
35680 * @cfg {String} href
35684 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35689 * @cfg {String} placetitle (center|bottom)
35694 * @cfg {Boolean} isFitContainer defalut true
35696 isFitContainer : true,
35699 * @cfg {Boolean} preventDefault defalut false
35701 preventDefault : false,
35704 * @cfg {Boolean} inverse defalut false
35706 maskInverse : false,
35708 getAutoCreate : function()
35710 if(!this.isFitContainer){
35711 return this.getSplitAutoCreate();
35714 var cls = 'masonry-brick masonry-brick-full';
35716 if(this.href.length){
35717 cls += ' masonry-brick-link';
35720 if(this.bgimage.length){
35721 cls += ' masonry-brick-image';
35724 if(this.maskInverse){
35725 cls += ' mask-inverse';
35728 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35729 cls += ' enable-mask';
35733 cls += ' masonry-' + this.size + '-brick';
35736 if(this.placetitle.length){
35738 switch (this.placetitle) {
35740 cls += ' masonry-center-title';
35743 cls += ' masonry-bottom-title';
35750 if(!this.html.length && !this.bgimage.length){
35751 cls += ' masonry-center-title';
35754 if(!this.html.length && this.bgimage.length){
35755 cls += ' masonry-bottom-title';
35760 cls += ' ' + this.cls;
35764 tag: (this.href.length) ? 'a' : 'div',
35769 cls: 'masonry-brick-mask'
35773 cls: 'masonry-brick-paragraph',
35779 if(this.href.length){
35780 cfg.href = this.href;
35783 var cn = cfg.cn[1].cn;
35785 if(this.title.length){
35788 cls: 'masonry-brick-title',
35793 if(this.html.length){
35796 cls: 'masonry-brick-text',
35801 if (!this.title.length && !this.html.length) {
35802 cfg.cn[1].cls += ' hide';
35805 if(this.bgimage.length){
35808 cls: 'masonry-brick-image-view',
35813 if(this.videourl.length){
35814 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35815 // youtube support only?
35818 cls: 'masonry-brick-image-view',
35821 allowfullscreen : true
35829 getSplitAutoCreate : function()
35831 var cls = 'masonry-brick masonry-brick-split';
35833 if(this.href.length){
35834 cls += ' masonry-brick-link';
35837 if(this.bgimage.length){
35838 cls += ' masonry-brick-image';
35842 cls += ' masonry-' + this.size + '-brick';
35845 switch (this.placetitle) {
35847 cls += ' masonry-center-title';
35850 cls += ' masonry-bottom-title';
35853 if(!this.bgimage.length){
35854 cls += ' masonry-center-title';
35857 if(this.bgimage.length){
35858 cls += ' masonry-bottom-title';
35864 cls += ' ' + this.cls;
35868 tag: (this.href.length) ? 'a' : 'div',
35873 cls: 'masonry-brick-split-head',
35877 cls: 'masonry-brick-paragraph',
35884 cls: 'masonry-brick-split-body',
35890 if(this.href.length){
35891 cfg.href = this.href;
35894 if(this.title.length){
35895 cfg.cn[0].cn[0].cn.push({
35897 cls: 'masonry-brick-title',
35902 if(this.html.length){
35903 cfg.cn[1].cn.push({
35905 cls: 'masonry-brick-text',
35910 if(this.bgimage.length){
35911 cfg.cn[0].cn.push({
35913 cls: 'masonry-brick-image-view',
35918 if(this.videourl.length){
35919 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35920 // youtube support only?
35921 cfg.cn[0].cn.cn.push({
35923 cls: 'masonry-brick-image-view',
35926 allowfullscreen : true
35933 initEvents: function()
35935 switch (this.size) {
35968 this.el.on('touchstart', this.onTouchStart, this);
35969 this.el.on('touchmove', this.onTouchMove, this);
35970 this.el.on('touchend', this.onTouchEnd, this);
35971 this.el.on('contextmenu', this.onContextMenu, this);
35973 this.el.on('mouseenter' ,this.enter, this);
35974 this.el.on('mouseleave', this.leave, this);
35975 this.el.on('click', this.onClick, this);
35978 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35979 this.parent().bricks.push(this);
35984 onClick: function(e, el)
35986 var time = this.endTimer - this.startTimer;
35987 // Roo.log(e.preventDefault());
35990 e.preventDefault();
35995 if(!this.preventDefault){
35999 e.preventDefault();
36001 if (this.activeClass != '') {
36002 this.selectBrick();
36005 this.fireEvent('click', this, e);
36008 enter: function(e, el)
36010 e.preventDefault();
36012 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36016 if(this.bgimage.length && this.html.length){
36017 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36021 leave: function(e, el)
36023 e.preventDefault();
36025 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36029 if(this.bgimage.length && this.html.length){
36030 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36034 onTouchStart: function(e, el)
36036 // e.preventDefault();
36038 this.touchmoved = false;
36040 if(!this.isFitContainer){
36044 if(!this.bgimage.length || !this.html.length){
36048 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36050 this.timer = new Date().getTime();
36054 onTouchMove: function(e, el)
36056 this.touchmoved = true;
36059 onContextMenu : function(e,el)
36061 e.preventDefault();
36062 e.stopPropagation();
36066 onTouchEnd: function(e, el)
36068 // e.preventDefault();
36070 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36077 if(!this.bgimage.length || !this.html.length){
36079 if(this.href.length){
36080 window.location.href = this.href;
36086 if(!this.isFitContainer){
36090 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36092 window.location.href = this.href;
36095 //selection on single brick only
36096 selectBrick : function() {
36098 if (!this.parentId) {
36102 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36103 var index = m.selectedBrick.indexOf(this.id);
36106 m.selectedBrick.splice(index,1);
36107 this.el.removeClass(this.activeClass);
36111 for(var i = 0; i < m.selectedBrick.length; i++) {
36112 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36113 b.el.removeClass(b.activeClass);
36116 m.selectedBrick = [];
36118 m.selectedBrick.push(this.id);
36119 this.el.addClass(this.activeClass);
36123 isSelected : function(){
36124 return this.el.hasClass(this.activeClass);
36129 Roo.apply(Roo.bootstrap.MasonryBrick, {
36132 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36134 * register a Masonry Brick
36135 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36138 register : function(brick)
36140 //this.groups[brick.id] = brick;
36141 this.groups.add(brick.id, brick);
36144 * fetch a masonry brick based on the masonry brick ID
36145 * @param {string} the masonry brick to add
36146 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36149 get: function(brick_id)
36151 // if (typeof(this.groups[brick_id]) == 'undefined') {
36154 // return this.groups[brick_id] ;
36156 if(this.groups.key(brick_id)) {
36157 return this.groups.key(brick_id);
36175 * @class Roo.bootstrap.Brick
36176 * @extends Roo.bootstrap.Component
36177 * Bootstrap Brick class
36180 * Create a new Brick
36181 * @param {Object} config The config object
36184 Roo.bootstrap.Brick = function(config){
36185 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36191 * When a Brick is click
36192 * @param {Roo.bootstrap.Brick} this
36193 * @param {Roo.EventObject} e
36199 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36202 * @cfg {String} title
36206 * @cfg {String} html
36210 * @cfg {String} bgimage
36214 * @cfg {String} cls
36218 * @cfg {String} href
36222 * @cfg {String} video
36226 * @cfg {Boolean} square
36230 getAutoCreate : function()
36232 var cls = 'roo-brick';
36234 if(this.href.length){
36235 cls += ' roo-brick-link';
36238 if(this.bgimage.length){
36239 cls += ' roo-brick-image';
36242 if(!this.html.length && !this.bgimage.length){
36243 cls += ' roo-brick-center-title';
36246 if(!this.html.length && this.bgimage.length){
36247 cls += ' roo-brick-bottom-title';
36251 cls += ' ' + this.cls;
36255 tag: (this.href.length) ? 'a' : 'div',
36260 cls: 'roo-brick-paragraph',
36266 if(this.href.length){
36267 cfg.href = this.href;
36270 var cn = cfg.cn[0].cn;
36272 if(this.title.length){
36275 cls: 'roo-brick-title',
36280 if(this.html.length){
36283 cls: 'roo-brick-text',
36290 if(this.bgimage.length){
36293 cls: 'roo-brick-image-view',
36301 initEvents: function()
36303 if(this.title.length || this.html.length){
36304 this.el.on('mouseenter' ,this.enter, this);
36305 this.el.on('mouseleave', this.leave, this);
36308 Roo.EventManager.onWindowResize(this.resize, this);
36310 if(this.bgimage.length){
36311 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36312 this.imageEl.on('load', this.onImageLoad, this);
36319 onImageLoad : function()
36324 resize : function()
36326 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36328 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36330 if(this.bgimage.length){
36331 var image = this.el.select('.roo-brick-image-view', true).first();
36333 image.setWidth(paragraph.getWidth());
36336 image.setHeight(paragraph.getWidth());
36339 this.el.setHeight(image.getHeight());
36340 paragraph.setHeight(image.getHeight());
36346 enter: function(e, el)
36348 e.preventDefault();
36350 if(this.bgimage.length){
36351 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36352 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36356 leave: function(e, el)
36358 e.preventDefault();
36360 if(this.bgimage.length){
36361 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36362 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36377 * @class Roo.bootstrap.NumberField
36378 * @extends Roo.bootstrap.Input
36379 * Bootstrap NumberField class
36385 * Create a new NumberField
36386 * @param {Object} config The config object
36389 Roo.bootstrap.NumberField = function(config){
36390 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36393 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36396 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36398 allowDecimals : true,
36400 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36402 decimalSeparator : ".",
36404 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36406 decimalPrecision : 2,
36408 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36410 allowNegative : true,
36413 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36417 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36419 minValue : Number.NEGATIVE_INFINITY,
36421 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36423 maxValue : Number.MAX_VALUE,
36425 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36427 minText : "The minimum value for this field is {0}",
36429 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36431 maxText : "The maximum value for this field is {0}",
36433 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36434 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36436 nanText : "{0} is not a valid number",
36438 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36440 thousandsDelimiter : false,
36442 * @cfg {String} valueAlign alignment of value
36444 valueAlign : "left",
36446 getAutoCreate : function()
36448 var hiddenInput = {
36452 cls: 'hidden-number-input'
36456 hiddenInput.name = this.name;
36461 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36463 this.name = hiddenInput.name;
36465 if(cfg.cn.length > 0) {
36466 cfg.cn.push(hiddenInput);
36473 initEvents : function()
36475 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36477 var allowed = "0123456789";
36479 if(this.allowDecimals){
36480 allowed += this.decimalSeparator;
36483 if(this.allowNegative){
36487 if(this.thousandsDelimiter) {
36491 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36493 var keyPress = function(e){
36495 var k = e.getKey();
36497 var c = e.getCharCode();
36500 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36501 allowed.indexOf(String.fromCharCode(c)) === -1
36507 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36511 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36516 this.el.on("keypress", keyPress, this);
36519 validateValue : function(value)
36522 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36526 var num = this.parseValue(value);
36529 this.markInvalid(String.format(this.nanText, value));
36533 if(num < this.minValue){
36534 this.markInvalid(String.format(this.minText, this.minValue));
36538 if(num > this.maxValue){
36539 this.markInvalid(String.format(this.maxText, this.maxValue));
36546 getValue : function()
36548 var v = this.hiddenEl().getValue();
36550 return this.fixPrecision(this.parseValue(v));
36553 parseValue : function(value)
36555 if(this.thousandsDelimiter) {
36557 r = new RegExp(",", "g");
36558 value = value.replace(r, "");
36561 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36562 return isNaN(value) ? '' : value;
36565 fixPrecision : function(value)
36567 if(this.thousandsDelimiter) {
36569 r = new RegExp(",", "g");
36570 value = value.replace(r, "");
36573 var nan = isNaN(value);
36575 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36576 return nan ? '' : value;
36578 return parseFloat(value).toFixed(this.decimalPrecision);
36581 setValue : function(v)
36583 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36589 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36591 this.inputEl().dom.value = (v == '') ? '' :
36592 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36594 if(!this.allowZero && v === '0') {
36595 this.hiddenEl().dom.value = '';
36596 this.inputEl().dom.value = '';
36603 decimalPrecisionFcn : function(v)
36605 return Math.floor(v);
36608 beforeBlur : function()
36610 var v = this.parseValue(this.getRawValue());
36612 if(v || v === 0 || v === ''){
36617 hiddenEl : function()
36619 return this.el.select('input.hidden-number-input',true).first();
36631 * @class Roo.bootstrap.DocumentSlider
36632 * @extends Roo.bootstrap.Component
36633 * Bootstrap DocumentSlider class
36636 * Create a new DocumentViewer
36637 * @param {Object} config The config object
36640 Roo.bootstrap.DocumentSlider = function(config){
36641 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36648 * Fire after initEvent
36649 * @param {Roo.bootstrap.DocumentSlider} this
36654 * Fire after update
36655 * @param {Roo.bootstrap.DocumentSlider} this
36661 * @param {Roo.bootstrap.DocumentSlider} this
36667 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36673 getAutoCreate : function()
36677 cls : 'roo-document-slider',
36681 cls : 'roo-document-slider-header',
36685 cls : 'roo-document-slider-header-title'
36691 cls : 'roo-document-slider-body',
36695 cls : 'roo-document-slider-prev',
36699 cls : 'fa fa-chevron-left'
36705 cls : 'roo-document-slider-thumb',
36709 cls : 'roo-document-slider-image'
36715 cls : 'roo-document-slider-next',
36719 cls : 'fa fa-chevron-right'
36731 initEvents : function()
36733 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36734 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36736 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36737 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36739 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36740 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36742 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36743 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36745 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36746 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36748 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36749 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36751 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36752 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36754 this.thumbEl.on('click', this.onClick, this);
36756 this.prevIndicator.on('click', this.prev, this);
36758 this.nextIndicator.on('click', this.next, this);
36762 initial : function()
36764 if(this.files.length){
36765 this.indicator = 1;
36769 this.fireEvent('initial', this);
36772 update : function()
36774 this.imageEl.attr('src', this.files[this.indicator - 1]);
36776 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36778 this.prevIndicator.show();
36780 if(this.indicator == 1){
36781 this.prevIndicator.hide();
36784 this.nextIndicator.show();
36786 if(this.indicator == this.files.length){
36787 this.nextIndicator.hide();
36790 this.thumbEl.scrollTo('top');
36792 this.fireEvent('update', this);
36795 onClick : function(e)
36797 e.preventDefault();
36799 this.fireEvent('click', this);
36804 e.preventDefault();
36806 this.indicator = Math.max(1, this.indicator - 1);
36813 e.preventDefault();
36815 this.indicator = Math.min(this.files.length, this.indicator + 1);
36829 * @class Roo.bootstrap.RadioSet
36830 * @extends Roo.bootstrap.Input
36831 * Bootstrap RadioSet class
36832 * @cfg {String} indicatorpos (left|right) default left
36833 * @cfg {Boolean} inline (true|false) inline the element (default true)
36834 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36836 * Create a new RadioSet
36837 * @param {Object} config The config object
36840 Roo.bootstrap.RadioSet = function(config){
36842 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36846 Roo.bootstrap.RadioSet.register(this);
36851 * Fires when the element is checked or unchecked.
36852 * @param {Roo.bootstrap.RadioSet} this This radio
36853 * @param {Roo.bootstrap.Radio} item The checked item
36858 * Fires when the element is click.
36859 * @param {Roo.bootstrap.RadioSet} this This radio set
36860 * @param {Roo.bootstrap.Radio} item The checked item
36861 * @param {Roo.EventObject} e The event object
36868 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36876 indicatorpos : 'left',
36878 getAutoCreate : function()
36882 cls : 'roo-radio-set-label',
36886 html : this.fieldLabel
36890 if (Roo.bootstrap.version == 3) {
36893 if(this.indicatorpos == 'left'){
36896 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36897 tooltip : 'This field is required'
36902 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36903 tooltip : 'This field is required'
36909 cls : 'roo-radio-set-items'
36912 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36914 if (align === 'left' && this.fieldLabel.length) {
36917 cls : "roo-radio-set-right",
36923 if(this.labelWidth > 12){
36924 label.style = "width: " + this.labelWidth + 'px';
36927 if(this.labelWidth < 13 && this.labelmd == 0){
36928 this.labelmd = this.labelWidth;
36931 if(this.labellg > 0){
36932 label.cls += ' col-lg-' + this.labellg;
36933 items.cls += ' col-lg-' + (12 - this.labellg);
36936 if(this.labelmd > 0){
36937 label.cls += ' col-md-' + this.labelmd;
36938 items.cls += ' col-md-' + (12 - this.labelmd);
36941 if(this.labelsm > 0){
36942 label.cls += ' col-sm-' + this.labelsm;
36943 items.cls += ' col-sm-' + (12 - this.labelsm);
36946 if(this.labelxs > 0){
36947 label.cls += ' col-xs-' + this.labelxs;
36948 items.cls += ' col-xs-' + (12 - this.labelxs);
36954 cls : 'roo-radio-set',
36958 cls : 'roo-radio-set-input',
36961 value : this.value ? this.value : ''
36968 if(this.weight.length){
36969 cfg.cls += ' roo-radio-' + this.weight;
36973 cfg.cls += ' roo-radio-set-inline';
36977 ['xs','sm','md','lg'].map(function(size){
36978 if (settings[size]) {
36979 cfg.cls += ' col-' + size + '-' + settings[size];
36987 initEvents : function()
36989 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36990 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36992 if(!this.fieldLabel.length){
36993 this.labelEl.hide();
36996 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36997 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36999 this.indicator = this.indicatorEl();
37001 if(this.indicator){
37002 this.indicator.addClass('invisible');
37005 this.originalValue = this.getValue();
37009 inputEl: function ()
37011 return this.el.select('.roo-radio-set-input', true).first();
37014 getChildContainer : function()
37016 return this.itemsEl;
37019 register : function(item)
37021 this.radioes.push(item);
37025 validate : function()
37027 if(this.getVisibilityEl().hasClass('hidden')){
37033 Roo.each(this.radioes, function(i){
37042 if(this.allowBlank) {
37046 if(this.disabled || valid){
37051 this.markInvalid();
37056 markValid : function()
37058 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37059 this.indicatorEl().removeClass('visible');
37060 this.indicatorEl().addClass('invisible');
37064 if (Roo.bootstrap.version == 3) {
37065 this.el.removeClass([this.invalidClass, this.validClass]);
37066 this.el.addClass(this.validClass);
37068 this.el.removeClass(['is-invalid','is-valid']);
37069 this.el.addClass(['is-valid']);
37071 this.fireEvent('valid', this);
37074 markInvalid : function(msg)
37076 if(this.allowBlank || this.disabled){
37080 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37081 this.indicatorEl().removeClass('invisible');
37082 this.indicatorEl().addClass('visible');
37084 if (Roo.bootstrap.version == 3) {
37085 this.el.removeClass([this.invalidClass, this.validClass]);
37086 this.el.addClass(this.invalidClass);
37088 this.el.removeClass(['is-invalid','is-valid']);
37089 this.el.addClass(['is-invalid']);
37092 this.fireEvent('invalid', this, msg);
37096 setValue : function(v, suppressEvent)
37098 if(this.value === v){
37105 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37108 Roo.each(this.radioes, function(i){
37110 i.el.removeClass('checked');
37113 Roo.each(this.radioes, function(i){
37115 if(i.value === v || i.value.toString() === v.toString()){
37117 i.el.addClass('checked');
37119 if(suppressEvent !== true){
37120 this.fireEvent('check', this, i);
37131 clearInvalid : function(){
37133 if(!this.el || this.preventMark){
37137 this.el.removeClass([this.invalidClass]);
37139 this.fireEvent('valid', this);
37144 Roo.apply(Roo.bootstrap.RadioSet, {
37148 register : function(set)
37150 this.groups[set.name] = set;
37153 get: function(name)
37155 if (typeof(this.groups[name]) == 'undefined') {
37159 return this.groups[name] ;
37165 * Ext JS Library 1.1.1
37166 * Copyright(c) 2006-2007, Ext JS, LLC.
37168 * Originally Released Under LGPL - original licence link has changed is not relivant.
37171 * <script type="text/javascript">
37176 * @class Roo.bootstrap.SplitBar
37177 * @extends Roo.util.Observable
37178 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37182 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37183 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37184 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37185 split.minSize = 100;
37186 split.maxSize = 600;
37187 split.animate = true;
37188 split.on('moved', splitterMoved);
37191 * Create a new SplitBar
37192 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37193 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37194 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37195 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37196 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37197 position of the SplitBar).
37199 Roo.bootstrap.SplitBar = function(cfg){
37204 // dragElement : elm
37205 // resizingElement: el,
37207 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37208 // placement : Roo.bootstrap.SplitBar.LEFT ,
37209 // existingProxy ???
37212 this.el = Roo.get(cfg.dragElement, true);
37213 this.el.dom.unselectable = "on";
37215 this.resizingEl = Roo.get(cfg.resizingElement, true);
37219 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37220 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37223 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37226 * The minimum size of the resizing element. (Defaults to 0)
37232 * The maximum size of the resizing element. (Defaults to 2000)
37235 this.maxSize = 2000;
37238 * Whether to animate the transition to the new size
37241 this.animate = false;
37244 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37247 this.useShim = false;
37252 if(!cfg.existingProxy){
37254 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37256 this.proxy = Roo.get(cfg.existingProxy).dom;
37259 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37262 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37265 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37268 this.dragSpecs = {};
37271 * @private The adapter to use to positon and resize elements
37273 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37274 this.adapter.init(this);
37276 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37278 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37279 this.el.addClass("roo-splitbar-h");
37282 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37283 this.el.addClass("roo-splitbar-v");
37289 * Fires when the splitter is moved (alias for {@link #event-moved})
37290 * @param {Roo.bootstrap.SplitBar} this
37291 * @param {Number} newSize the new width or height
37296 * Fires when the splitter is moved
37297 * @param {Roo.bootstrap.SplitBar} this
37298 * @param {Number} newSize the new width or height
37302 * @event beforeresize
37303 * Fires before the splitter is dragged
37304 * @param {Roo.bootstrap.SplitBar} this
37306 "beforeresize" : true,
37308 "beforeapply" : true
37311 Roo.util.Observable.call(this);
37314 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37315 onStartProxyDrag : function(x, y){
37316 this.fireEvent("beforeresize", this);
37318 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37320 o.enableDisplayMode("block");
37321 // all splitbars share the same overlay
37322 Roo.bootstrap.SplitBar.prototype.overlay = o;
37324 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37325 this.overlay.show();
37326 Roo.get(this.proxy).setDisplayed("block");
37327 var size = this.adapter.getElementSize(this);
37328 this.activeMinSize = this.getMinimumSize();;
37329 this.activeMaxSize = this.getMaximumSize();;
37330 var c1 = size - this.activeMinSize;
37331 var c2 = Math.max(this.activeMaxSize - size, 0);
37332 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37333 this.dd.resetConstraints();
37334 this.dd.setXConstraint(
37335 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37336 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37338 this.dd.setYConstraint(0, 0);
37340 this.dd.resetConstraints();
37341 this.dd.setXConstraint(0, 0);
37342 this.dd.setYConstraint(
37343 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37344 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37347 this.dragSpecs.startSize = size;
37348 this.dragSpecs.startPoint = [x, y];
37349 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37353 * @private Called after the drag operation by the DDProxy
37355 onEndProxyDrag : function(e){
37356 Roo.get(this.proxy).setDisplayed(false);
37357 var endPoint = Roo.lib.Event.getXY(e);
37359 this.overlay.hide();
37362 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37363 newSize = this.dragSpecs.startSize +
37364 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37365 endPoint[0] - this.dragSpecs.startPoint[0] :
37366 this.dragSpecs.startPoint[0] - endPoint[0]
37369 newSize = this.dragSpecs.startSize +
37370 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37371 endPoint[1] - this.dragSpecs.startPoint[1] :
37372 this.dragSpecs.startPoint[1] - endPoint[1]
37375 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37376 if(newSize != this.dragSpecs.startSize){
37377 if(this.fireEvent('beforeapply', this, newSize) !== false){
37378 this.adapter.setElementSize(this, newSize);
37379 this.fireEvent("moved", this, newSize);
37380 this.fireEvent("resize", this, newSize);
37386 * Get the adapter this SplitBar uses
37387 * @return The adapter object
37389 getAdapter : function(){
37390 return this.adapter;
37394 * Set the adapter this SplitBar uses
37395 * @param {Object} adapter A SplitBar adapter object
37397 setAdapter : function(adapter){
37398 this.adapter = adapter;
37399 this.adapter.init(this);
37403 * Gets the minimum size for the resizing element
37404 * @return {Number} The minimum size
37406 getMinimumSize : function(){
37407 return this.minSize;
37411 * Sets the minimum size for the resizing element
37412 * @param {Number} minSize The minimum size
37414 setMinimumSize : function(minSize){
37415 this.minSize = minSize;
37419 * Gets the maximum size for the resizing element
37420 * @return {Number} The maximum size
37422 getMaximumSize : function(){
37423 return this.maxSize;
37427 * Sets the maximum size for the resizing element
37428 * @param {Number} maxSize The maximum size
37430 setMaximumSize : function(maxSize){
37431 this.maxSize = maxSize;
37435 * Sets the initialize size for the resizing element
37436 * @param {Number} size The initial size
37438 setCurrentSize : function(size){
37439 var oldAnimate = this.animate;
37440 this.animate = false;
37441 this.adapter.setElementSize(this, size);
37442 this.animate = oldAnimate;
37446 * Destroy this splitbar.
37447 * @param {Boolean} removeEl True to remove the element
37449 destroy : function(removeEl){
37451 this.shim.remove();
37454 this.proxy.parentNode.removeChild(this.proxy);
37462 * @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.
37464 Roo.bootstrap.SplitBar.createProxy = function(dir){
37465 var proxy = new Roo.Element(document.createElement("div"));
37466 proxy.unselectable();
37467 var cls = 'roo-splitbar-proxy';
37468 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37469 document.body.appendChild(proxy.dom);
37474 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37475 * Default Adapter. It assumes the splitter and resizing element are not positioned
37476 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37478 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37481 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37482 // do nothing for now
37483 init : function(s){
37487 * Called before drag operations to get the current size of the resizing element.
37488 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37490 getElementSize : function(s){
37491 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37492 return s.resizingEl.getWidth();
37494 return s.resizingEl.getHeight();
37499 * Called after drag operations to set the size of the resizing element.
37500 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37501 * @param {Number} newSize The new size to set
37502 * @param {Function} onComplete A function to be invoked when resizing is complete
37504 setElementSize : function(s, newSize, onComplete){
37505 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37507 s.resizingEl.setWidth(newSize);
37509 onComplete(s, newSize);
37512 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37517 s.resizingEl.setHeight(newSize);
37519 onComplete(s, newSize);
37522 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37529 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37530 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37531 * Adapter that moves the splitter element to align with the resized sizing element.
37532 * Used with an absolute positioned SplitBar.
37533 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37534 * document.body, make sure you assign an id to the body element.
37536 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37537 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37538 this.container = Roo.get(container);
37541 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37542 init : function(s){
37543 this.basic.init(s);
37546 getElementSize : function(s){
37547 return this.basic.getElementSize(s);
37550 setElementSize : function(s, newSize, onComplete){
37551 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37554 moveSplitter : function(s){
37555 var yes = Roo.bootstrap.SplitBar;
37556 switch(s.placement){
37558 s.el.setX(s.resizingEl.getRight());
37561 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37564 s.el.setY(s.resizingEl.getBottom());
37567 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37574 * Orientation constant - Create a vertical SplitBar
37578 Roo.bootstrap.SplitBar.VERTICAL = 1;
37581 * Orientation constant - Create a horizontal SplitBar
37585 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37588 * Placement constant - The resizing element is to the left of the splitter element
37592 Roo.bootstrap.SplitBar.LEFT = 1;
37595 * Placement constant - The resizing element is to the right of the splitter element
37599 Roo.bootstrap.SplitBar.RIGHT = 2;
37602 * Placement constant - The resizing element is positioned above the splitter element
37606 Roo.bootstrap.SplitBar.TOP = 3;
37609 * Placement constant - The resizing element is positioned under splitter element
37613 Roo.bootstrap.SplitBar.BOTTOM = 4;
37614 Roo.namespace("Roo.bootstrap.layout");/*
37616 * Ext JS Library 1.1.1
37617 * Copyright(c) 2006-2007, Ext JS, LLC.
37619 * Originally Released Under LGPL - original licence link has changed is not relivant.
37622 * <script type="text/javascript">
37626 * @class Roo.bootstrap.layout.Manager
37627 * @extends Roo.bootstrap.Component
37628 * Base class for layout managers.
37630 Roo.bootstrap.layout.Manager = function(config)
37632 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37638 /** false to disable window resize monitoring @type Boolean */
37639 this.monitorWindowResize = true;
37644 * Fires when a layout is performed.
37645 * @param {Roo.LayoutManager} this
37649 * @event regionresized
37650 * Fires when the user resizes a region.
37651 * @param {Roo.LayoutRegion} region The resized region
37652 * @param {Number} newSize The new size (width for east/west, height for north/south)
37654 "regionresized" : true,
37656 * @event regioncollapsed
37657 * Fires when a region is collapsed.
37658 * @param {Roo.LayoutRegion} region The collapsed region
37660 "regioncollapsed" : true,
37662 * @event regionexpanded
37663 * Fires when a region is expanded.
37664 * @param {Roo.LayoutRegion} region The expanded region
37666 "regionexpanded" : true
37668 this.updating = false;
37671 this.el = Roo.get(config.el);
37677 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37682 monitorWindowResize : true,
37688 onRender : function(ct, position)
37691 this.el = Roo.get(ct);
37694 //this.fireEvent('render',this);
37698 initEvents: function()
37702 // ie scrollbar fix
37703 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37704 document.body.scroll = "no";
37705 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37706 this.el.position('relative');
37708 this.id = this.el.id;
37709 this.el.addClass("roo-layout-container");
37710 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37711 if(this.el.dom != document.body ) {
37712 this.el.on('resize', this.layout,this);
37713 this.el.on('show', this.layout,this);
37719 * Returns true if this layout is currently being updated
37720 * @return {Boolean}
37722 isUpdating : function(){
37723 return this.updating;
37727 * Suspend the LayoutManager from doing auto-layouts while
37728 * making multiple add or remove calls
37730 beginUpdate : function(){
37731 this.updating = true;
37735 * Restore auto-layouts and optionally disable the manager from performing a layout
37736 * @param {Boolean} noLayout true to disable a layout update
37738 endUpdate : function(noLayout){
37739 this.updating = false;
37745 layout: function(){
37749 onRegionResized : function(region, newSize){
37750 this.fireEvent("regionresized", region, newSize);
37754 onRegionCollapsed : function(region){
37755 this.fireEvent("regioncollapsed", region);
37758 onRegionExpanded : function(region){
37759 this.fireEvent("regionexpanded", region);
37763 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37764 * performs box-model adjustments.
37765 * @return {Object} The size as an object {width: (the width), height: (the height)}
37767 getViewSize : function()
37770 if(this.el.dom != document.body){
37771 size = this.el.getSize();
37773 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37775 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37776 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37781 * Returns the Element this layout is bound to.
37782 * @return {Roo.Element}
37784 getEl : function(){
37789 * Returns the specified region.
37790 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37791 * @return {Roo.LayoutRegion}
37793 getRegion : function(target){
37794 return this.regions[target.toLowerCase()];
37797 onWindowResize : function(){
37798 if(this.monitorWindowResize){
37805 * Ext JS Library 1.1.1
37806 * Copyright(c) 2006-2007, Ext JS, LLC.
37808 * Originally Released Under LGPL - original licence link has changed is not relivant.
37811 * <script type="text/javascript">
37814 * @class Roo.bootstrap.layout.Border
37815 * @extends Roo.bootstrap.layout.Manager
37816 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37817 * please see: examples/bootstrap/nested.html<br><br>
37819 <b>The container the layout is rendered into can be either the body element or any other element.
37820 If it is not the body element, the container needs to either be an absolute positioned element,
37821 or you will need to add "position:relative" to the css of the container. You will also need to specify
37822 the container size if it is not the body element.</b>
37825 * Create a new Border
37826 * @param {Object} config Configuration options
37828 Roo.bootstrap.layout.Border = function(config){
37829 config = config || {};
37830 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37834 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37835 if(config[region]){
37836 config[region].region = region;
37837 this.addRegion(config[region]);
37843 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37845 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37847 parent : false, // this might point to a 'nest' or a ???
37850 * Creates and adds a new region if it doesn't already exist.
37851 * @param {String} target The target region key (north, south, east, west or center).
37852 * @param {Object} config The regions config object
37853 * @return {BorderLayoutRegion} The new region
37855 addRegion : function(config)
37857 if(!this.regions[config.region]){
37858 var r = this.factory(config);
37859 this.bindRegion(r);
37861 return this.regions[config.region];
37865 bindRegion : function(r){
37866 this.regions[r.config.region] = r;
37868 r.on("visibilitychange", this.layout, this);
37869 r.on("paneladded", this.layout, this);
37870 r.on("panelremoved", this.layout, this);
37871 r.on("invalidated", this.layout, this);
37872 r.on("resized", this.onRegionResized, this);
37873 r.on("collapsed", this.onRegionCollapsed, this);
37874 r.on("expanded", this.onRegionExpanded, this);
37878 * Performs a layout update.
37880 layout : function()
37882 if(this.updating) {
37886 // render all the rebions if they have not been done alreayd?
37887 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37888 if(this.regions[region] && !this.regions[region].bodyEl){
37889 this.regions[region].onRender(this.el)
37893 var size = this.getViewSize();
37894 var w = size.width;
37895 var h = size.height;
37900 //var x = 0, y = 0;
37902 var rs = this.regions;
37903 var north = rs["north"];
37904 var south = rs["south"];
37905 var west = rs["west"];
37906 var east = rs["east"];
37907 var center = rs["center"];
37908 //if(this.hideOnLayout){ // not supported anymore
37909 //c.el.setStyle("display", "none");
37911 if(north && north.isVisible()){
37912 var b = north.getBox();
37913 var m = north.getMargins();
37914 b.width = w - (m.left+m.right);
37917 centerY = b.height + b.y + m.bottom;
37918 centerH -= centerY;
37919 north.updateBox(this.safeBox(b));
37921 if(south && south.isVisible()){
37922 var b = south.getBox();
37923 var m = south.getMargins();
37924 b.width = w - (m.left+m.right);
37926 var totalHeight = (b.height + m.top + m.bottom);
37927 b.y = h - totalHeight + m.top;
37928 centerH -= totalHeight;
37929 south.updateBox(this.safeBox(b));
37931 if(west && west.isVisible()){
37932 var b = west.getBox();
37933 var m = west.getMargins();
37934 b.height = centerH - (m.top+m.bottom);
37936 b.y = centerY + m.top;
37937 var totalWidth = (b.width + m.left + m.right);
37938 centerX += totalWidth;
37939 centerW -= totalWidth;
37940 west.updateBox(this.safeBox(b));
37942 if(east && east.isVisible()){
37943 var b = east.getBox();
37944 var m = east.getMargins();
37945 b.height = centerH - (m.top+m.bottom);
37946 var totalWidth = (b.width + m.left + m.right);
37947 b.x = w - totalWidth + m.left;
37948 b.y = centerY + m.top;
37949 centerW -= totalWidth;
37950 east.updateBox(this.safeBox(b));
37953 var m = center.getMargins();
37955 x: centerX + m.left,
37956 y: centerY + m.top,
37957 width: centerW - (m.left+m.right),
37958 height: centerH - (m.top+m.bottom)
37960 //if(this.hideOnLayout){
37961 //center.el.setStyle("display", "block");
37963 center.updateBox(this.safeBox(centerBox));
37966 this.fireEvent("layout", this);
37970 safeBox : function(box){
37971 box.width = Math.max(0, box.width);
37972 box.height = Math.max(0, box.height);
37977 * Adds a ContentPanel (or subclass) to this layout.
37978 * @param {String} target The target region key (north, south, east, west or center).
37979 * @param {Roo.ContentPanel} panel The panel to add
37980 * @return {Roo.ContentPanel} The added panel
37982 add : function(target, panel){
37984 target = target.toLowerCase();
37985 return this.regions[target].add(panel);
37989 * Remove a ContentPanel (or subclass) to this layout.
37990 * @param {String} target The target region key (north, south, east, west or center).
37991 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37992 * @return {Roo.ContentPanel} The removed panel
37994 remove : function(target, panel){
37995 target = target.toLowerCase();
37996 return this.regions[target].remove(panel);
38000 * Searches all regions for a panel with the specified id
38001 * @param {String} panelId
38002 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38004 findPanel : function(panelId){
38005 var rs = this.regions;
38006 for(var target in rs){
38007 if(typeof rs[target] != "function"){
38008 var p = rs[target].getPanel(panelId);
38018 * Searches all regions for a panel with the specified id and activates (shows) it.
38019 * @param {String/ContentPanel} panelId The panels id or the panel itself
38020 * @return {Roo.ContentPanel} The shown panel or null
38022 showPanel : function(panelId) {
38023 var rs = this.regions;
38024 for(var target in rs){
38025 var r = rs[target];
38026 if(typeof r != "function"){
38027 if(r.hasPanel(panelId)){
38028 return r.showPanel(panelId);
38036 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38037 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38040 restoreState : function(provider){
38042 provider = Roo.state.Manager;
38044 var sm = new Roo.LayoutStateManager();
38045 sm.init(this, provider);
38051 * Adds a xtype elements to the layout.
38055 xtype : 'ContentPanel',
38062 xtype : 'NestedLayoutPanel',
38068 items : [ ... list of content panels or nested layout panels.. ]
38072 * @param {Object} cfg Xtype definition of item to add.
38074 addxtype : function(cfg)
38076 // basically accepts a pannel...
38077 // can accept a layout region..!?!?
38078 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38081 // theory? children can only be panels??
38083 //if (!cfg.xtype.match(/Panel$/)) {
38088 if (typeof(cfg.region) == 'undefined') {
38089 Roo.log("Failed to add Panel, region was not set");
38093 var region = cfg.region;
38099 xitems = cfg.items;
38104 if ( region == 'center') {
38105 Roo.log("Center: " + cfg.title);
38111 case 'Content': // ContentPanel (el, cfg)
38112 case 'Scroll': // ContentPanel (el, cfg)
38114 cfg.autoCreate = cfg.autoCreate || true;
38115 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38117 // var el = this.el.createChild();
38118 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38121 this.add(region, ret);
38125 case 'TreePanel': // our new panel!
38126 cfg.el = this.el.createChild();
38127 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38128 this.add(region, ret);
38133 // create a new Layout (which is a Border Layout...
38135 var clayout = cfg.layout;
38136 clayout.el = this.el.createChild();
38137 clayout.items = clayout.items || [];
38141 // replace this exitems with the clayout ones..
38142 xitems = clayout.items;
38144 // force background off if it's in center...
38145 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38146 cfg.background = false;
38148 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38151 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38152 //console.log('adding nested layout panel ' + cfg.toSource());
38153 this.add(region, ret);
38154 nb = {}; /// find first...
38159 // needs grid and region
38161 //var el = this.getRegion(region).el.createChild();
38163 *var el = this.el.createChild();
38164 // create the grid first...
38165 cfg.grid.container = el;
38166 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38169 if (region == 'center' && this.active ) {
38170 cfg.background = false;
38173 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38175 this.add(region, ret);
38177 if (cfg.background) {
38178 // render grid on panel activation (if panel background)
38179 ret.on('activate', function(gp) {
38180 if (!gp.grid.rendered) {
38181 // gp.grid.render(el);
38185 // cfg.grid.render(el);
38191 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38192 // it was the old xcomponent building that caused this before.
38193 // espeically if border is the top element in the tree.
38203 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38205 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38206 this.add(region, ret);
38210 throw "Can not add '" + cfg.xtype + "' to Border";
38216 this.beginUpdate();
38220 Roo.each(xitems, function(i) {
38221 region = nb && i.region ? i.region : false;
38223 var add = ret.addxtype(i);
38226 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38227 if (!i.background) {
38228 abn[region] = nb[region] ;
38235 // make the last non-background panel active..
38236 //if (nb) { Roo.log(abn); }
38239 for(var r in abn) {
38240 region = this.getRegion(r);
38242 // tried using nb[r], but it does not work..
38244 region.showPanel(abn[r]);
38255 factory : function(cfg)
38258 var validRegions = Roo.bootstrap.layout.Border.regions;
38260 var target = cfg.region;
38263 var r = Roo.bootstrap.layout;
38267 return new r.North(cfg);
38269 return new r.South(cfg);
38271 return new r.East(cfg);
38273 return new r.West(cfg);
38275 return new r.Center(cfg);
38277 throw 'Layout region "'+target+'" not supported.';
38284 * Ext JS Library 1.1.1
38285 * Copyright(c) 2006-2007, Ext JS, LLC.
38287 * Originally Released Under LGPL - original licence link has changed is not relivant.
38290 * <script type="text/javascript">
38294 * @class Roo.bootstrap.layout.Basic
38295 * @extends Roo.util.Observable
38296 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38297 * and does not have a titlebar, tabs or any other features. All it does is size and position
38298 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38299 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38300 * @cfg {string} region the region that it inhabits..
38301 * @cfg {bool} skipConfig skip config?
38305 Roo.bootstrap.layout.Basic = function(config){
38307 this.mgr = config.mgr;
38309 this.position = config.region;
38311 var skipConfig = config.skipConfig;
38315 * @scope Roo.BasicLayoutRegion
38319 * @event beforeremove
38320 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38321 * @param {Roo.LayoutRegion} this
38322 * @param {Roo.ContentPanel} panel The panel
38323 * @param {Object} e The cancel event object
38325 "beforeremove" : true,
38327 * @event invalidated
38328 * Fires when the layout for this region is changed.
38329 * @param {Roo.LayoutRegion} this
38331 "invalidated" : true,
38333 * @event visibilitychange
38334 * Fires when this region is shown or hidden
38335 * @param {Roo.LayoutRegion} this
38336 * @param {Boolean} visibility true or false
38338 "visibilitychange" : true,
38340 * @event paneladded
38341 * Fires when a panel is added.
38342 * @param {Roo.LayoutRegion} this
38343 * @param {Roo.ContentPanel} panel The panel
38345 "paneladded" : true,
38347 * @event panelremoved
38348 * Fires when a panel is removed.
38349 * @param {Roo.LayoutRegion} this
38350 * @param {Roo.ContentPanel} panel The panel
38352 "panelremoved" : true,
38354 * @event beforecollapse
38355 * Fires when this region before collapse.
38356 * @param {Roo.LayoutRegion} this
38358 "beforecollapse" : true,
38361 * Fires when this region is collapsed.
38362 * @param {Roo.LayoutRegion} this
38364 "collapsed" : true,
38367 * Fires when this region is expanded.
38368 * @param {Roo.LayoutRegion} this
38373 * Fires when this region is slid into view.
38374 * @param {Roo.LayoutRegion} this
38376 "slideshow" : true,
38379 * Fires when this region slides out of view.
38380 * @param {Roo.LayoutRegion} this
38382 "slidehide" : true,
38384 * @event panelactivated
38385 * Fires when a panel is activated.
38386 * @param {Roo.LayoutRegion} this
38387 * @param {Roo.ContentPanel} panel The activated panel
38389 "panelactivated" : true,
38392 * Fires when the user resizes this region.
38393 * @param {Roo.LayoutRegion} this
38394 * @param {Number} newSize The new size (width for east/west, height for north/south)
38398 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38399 this.panels = new Roo.util.MixedCollection();
38400 this.panels.getKey = this.getPanelId.createDelegate(this);
38402 this.activePanel = null;
38403 // ensure listeners are added...
38405 if (config.listeners || config.events) {
38406 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38407 listeners : config.listeners || {},
38408 events : config.events || {}
38412 if(skipConfig !== true){
38413 this.applyConfig(config);
38417 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38419 getPanelId : function(p){
38423 applyConfig : function(config){
38424 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38425 this.config = config;
38430 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38431 * the width, for horizontal (north, south) the height.
38432 * @param {Number} newSize The new width or height
38434 resizeTo : function(newSize){
38435 var el = this.el ? this.el :
38436 (this.activePanel ? this.activePanel.getEl() : null);
38438 switch(this.position){
38441 el.setWidth(newSize);
38442 this.fireEvent("resized", this, newSize);
38446 el.setHeight(newSize);
38447 this.fireEvent("resized", this, newSize);
38453 getBox : function(){
38454 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38457 getMargins : function(){
38458 return this.margins;
38461 updateBox : function(box){
38463 var el = this.activePanel.getEl();
38464 el.dom.style.left = box.x + "px";
38465 el.dom.style.top = box.y + "px";
38466 this.activePanel.setSize(box.width, box.height);
38470 * Returns the container element for this region.
38471 * @return {Roo.Element}
38473 getEl : function(){
38474 return this.activePanel;
38478 * Returns true if this region is currently visible.
38479 * @return {Boolean}
38481 isVisible : function(){
38482 return this.activePanel ? true : false;
38485 setActivePanel : function(panel){
38486 panel = this.getPanel(panel);
38487 if(this.activePanel && this.activePanel != panel){
38488 this.activePanel.setActiveState(false);
38489 this.activePanel.getEl().setLeftTop(-10000,-10000);
38491 this.activePanel = panel;
38492 panel.setActiveState(true);
38494 panel.setSize(this.box.width, this.box.height);
38496 this.fireEvent("panelactivated", this, panel);
38497 this.fireEvent("invalidated");
38501 * Show the specified panel.
38502 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38503 * @return {Roo.ContentPanel} The shown panel or null
38505 showPanel : function(panel){
38506 panel = this.getPanel(panel);
38508 this.setActivePanel(panel);
38514 * Get the active panel for this region.
38515 * @return {Roo.ContentPanel} The active panel or null
38517 getActivePanel : function(){
38518 return this.activePanel;
38522 * Add the passed ContentPanel(s)
38523 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38524 * @return {Roo.ContentPanel} The panel added (if only one was added)
38526 add : function(panel){
38527 if(arguments.length > 1){
38528 for(var i = 0, len = arguments.length; i < len; i++) {
38529 this.add(arguments[i]);
38533 if(this.hasPanel(panel)){
38534 this.showPanel(panel);
38537 var el = panel.getEl();
38538 if(el.dom.parentNode != this.mgr.el.dom){
38539 this.mgr.el.dom.appendChild(el.dom);
38541 if(panel.setRegion){
38542 panel.setRegion(this);
38544 this.panels.add(panel);
38545 el.setStyle("position", "absolute");
38546 if(!panel.background){
38547 this.setActivePanel(panel);
38548 if(this.config.initialSize && this.panels.getCount()==1){
38549 this.resizeTo(this.config.initialSize);
38552 this.fireEvent("paneladded", this, panel);
38557 * Returns true if the panel is in this region.
38558 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38559 * @return {Boolean}
38561 hasPanel : function(panel){
38562 if(typeof panel == "object"){ // must be panel obj
38563 panel = panel.getId();
38565 return this.getPanel(panel) ? true : false;
38569 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38570 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38571 * @param {Boolean} preservePanel Overrides the config preservePanel option
38572 * @return {Roo.ContentPanel} The panel that was removed
38574 remove : function(panel, preservePanel){
38575 panel = this.getPanel(panel);
38580 this.fireEvent("beforeremove", this, panel, e);
38581 if(e.cancel === true){
38584 var panelId = panel.getId();
38585 this.panels.removeKey(panelId);
38590 * Returns the panel specified or null if it's not in this region.
38591 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38592 * @return {Roo.ContentPanel}
38594 getPanel : function(id){
38595 if(typeof id == "object"){ // must be panel obj
38598 return this.panels.get(id);
38602 * Returns this regions position (north/south/east/west/center).
38605 getPosition: function(){
38606 return this.position;
38610 * Ext JS Library 1.1.1
38611 * Copyright(c) 2006-2007, Ext JS, LLC.
38613 * Originally Released Under LGPL - original licence link has changed is not relivant.
38616 * <script type="text/javascript">
38620 * @class Roo.bootstrap.layout.Region
38621 * @extends Roo.bootstrap.layout.Basic
38622 * This class represents a region in a layout manager.
38624 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38625 * @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})
38626 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38627 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38628 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38629 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38630 * @cfg {String} title The title for the region (overrides panel titles)
38631 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38632 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38633 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38634 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38635 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38636 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38637 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38638 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38639 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38640 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38642 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38643 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38644 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38645 * @cfg {Number} width For East/West panels
38646 * @cfg {Number} height For North/South panels
38647 * @cfg {Boolean} split To show the splitter
38648 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38650 * @cfg {string} cls Extra CSS classes to add to region
38652 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38653 * @cfg {string} region the region that it inhabits..
38656 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38657 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38659 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38660 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38661 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38663 Roo.bootstrap.layout.Region = function(config)
38665 this.applyConfig(config);
38667 var mgr = config.mgr;
38668 var pos = config.region;
38669 config.skipConfig = true;
38670 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38673 this.onRender(mgr.el);
38676 this.visible = true;
38677 this.collapsed = false;
38678 this.unrendered_panels = [];
38681 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38683 position: '', // set by wrapper (eg. north/south etc..)
38684 unrendered_panels : null, // unrendered panels.
38686 tabPosition : false,
38688 mgr: false, // points to 'Border'
38691 createBody : function(){
38692 /** This region's body element
38693 * @type Roo.Element */
38694 this.bodyEl = this.el.createChild({
38696 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38700 onRender: function(ctr, pos)
38702 var dh = Roo.DomHelper;
38703 /** This region's container element
38704 * @type Roo.Element */
38705 this.el = dh.append(ctr.dom, {
38707 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38709 /** This region's title element
38710 * @type Roo.Element */
38712 this.titleEl = dh.append(this.el.dom, {
38714 unselectable: "on",
38715 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38717 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38718 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38722 this.titleEl.enableDisplayMode();
38723 /** This region's title text element
38724 * @type HTMLElement */
38725 this.titleTextEl = this.titleEl.dom.firstChild;
38726 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38728 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38729 this.closeBtn.enableDisplayMode();
38730 this.closeBtn.on("click", this.closeClicked, this);
38731 this.closeBtn.hide();
38733 this.createBody(this.config);
38734 if(this.config.hideWhenEmpty){
38736 this.on("paneladded", this.validateVisibility, this);
38737 this.on("panelremoved", this.validateVisibility, this);
38739 if(this.autoScroll){
38740 this.bodyEl.setStyle("overflow", "auto");
38742 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38744 //if(c.titlebar !== false){
38745 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38746 this.titleEl.hide();
38748 this.titleEl.show();
38749 if(this.config.title){
38750 this.titleTextEl.innerHTML = this.config.title;
38754 if(this.config.collapsed){
38755 this.collapse(true);
38757 if(this.config.hidden){
38761 if (this.unrendered_panels && this.unrendered_panels.length) {
38762 for (var i =0;i< this.unrendered_panels.length; i++) {
38763 this.add(this.unrendered_panels[i]);
38765 this.unrendered_panels = null;
38771 applyConfig : function(c)
38774 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38775 var dh = Roo.DomHelper;
38776 if(c.titlebar !== false){
38777 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38778 this.collapseBtn.on("click", this.collapse, this);
38779 this.collapseBtn.enableDisplayMode();
38781 if(c.showPin === true || this.showPin){
38782 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38783 this.stickBtn.enableDisplayMode();
38784 this.stickBtn.on("click", this.expand, this);
38785 this.stickBtn.hide();
38790 /** This region's collapsed element
38791 * @type Roo.Element */
38794 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38795 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38798 if(c.floatable !== false){
38799 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38800 this.collapsedEl.on("click", this.collapseClick, this);
38803 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38804 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38805 id: "message", unselectable: "on", style:{"float":"left"}});
38806 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38808 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38809 this.expandBtn.on("click", this.expand, this);
38813 if(this.collapseBtn){
38814 this.collapseBtn.setVisible(c.collapsible == true);
38817 this.cmargins = c.cmargins || this.cmargins ||
38818 (this.position == "west" || this.position == "east" ?
38819 {top: 0, left: 2, right:2, bottom: 0} :
38820 {top: 2, left: 0, right:0, bottom: 2});
38822 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38825 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38827 this.autoScroll = c.autoScroll || false;
38832 this.duration = c.duration || .30;
38833 this.slideDuration = c.slideDuration || .45;
38838 * Returns true if this region is currently visible.
38839 * @return {Boolean}
38841 isVisible : function(){
38842 return this.visible;
38846 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38847 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38849 //setCollapsedTitle : function(title){
38850 // title = title || " ";
38851 // if(this.collapsedTitleTextEl){
38852 // this.collapsedTitleTextEl.innerHTML = title;
38856 getBox : function(){
38858 // if(!this.collapsed){
38859 b = this.el.getBox(false, true);
38861 // b = this.collapsedEl.getBox(false, true);
38866 getMargins : function(){
38867 return this.margins;
38868 //return this.collapsed ? this.cmargins : this.margins;
38871 highlight : function(){
38872 this.el.addClass("x-layout-panel-dragover");
38875 unhighlight : function(){
38876 this.el.removeClass("x-layout-panel-dragover");
38879 updateBox : function(box)
38881 if (!this.bodyEl) {
38882 return; // not rendered yet..
38886 if(!this.collapsed){
38887 this.el.dom.style.left = box.x + "px";
38888 this.el.dom.style.top = box.y + "px";
38889 this.updateBody(box.width, box.height);
38891 this.collapsedEl.dom.style.left = box.x + "px";
38892 this.collapsedEl.dom.style.top = box.y + "px";
38893 this.collapsedEl.setSize(box.width, box.height);
38896 this.tabs.autoSizeTabs();
38900 updateBody : function(w, h)
38903 this.el.setWidth(w);
38904 w -= this.el.getBorderWidth("rl");
38905 if(this.config.adjustments){
38906 w += this.config.adjustments[0];
38909 if(h !== null && h > 0){
38910 this.el.setHeight(h);
38911 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38912 h -= this.el.getBorderWidth("tb");
38913 if(this.config.adjustments){
38914 h += this.config.adjustments[1];
38916 this.bodyEl.setHeight(h);
38918 h = this.tabs.syncHeight(h);
38921 if(this.panelSize){
38922 w = w !== null ? w : this.panelSize.width;
38923 h = h !== null ? h : this.panelSize.height;
38925 if(this.activePanel){
38926 var el = this.activePanel.getEl();
38927 w = w !== null ? w : el.getWidth();
38928 h = h !== null ? h : el.getHeight();
38929 this.panelSize = {width: w, height: h};
38930 this.activePanel.setSize(w, h);
38932 if(Roo.isIE && this.tabs){
38933 this.tabs.el.repaint();
38938 * Returns the container element for this region.
38939 * @return {Roo.Element}
38941 getEl : function(){
38946 * Hides this region.
38949 //if(!this.collapsed){
38950 this.el.dom.style.left = "-2000px";
38953 // this.collapsedEl.dom.style.left = "-2000px";
38954 // this.collapsedEl.hide();
38956 this.visible = false;
38957 this.fireEvent("visibilitychange", this, false);
38961 * Shows this region if it was previously hidden.
38964 //if(!this.collapsed){
38967 // this.collapsedEl.show();
38969 this.visible = true;
38970 this.fireEvent("visibilitychange", this, true);
38973 closeClicked : function(){
38974 if(this.activePanel){
38975 this.remove(this.activePanel);
38979 collapseClick : function(e){
38981 e.stopPropagation();
38984 e.stopPropagation();
38990 * Collapses this region.
38991 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38994 collapse : function(skipAnim, skipCheck = false){
38995 if(this.collapsed) {
38999 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39001 this.collapsed = true;
39003 this.split.el.hide();
39005 if(this.config.animate && skipAnim !== true){
39006 this.fireEvent("invalidated", this);
39007 this.animateCollapse();
39009 this.el.setLocation(-20000,-20000);
39011 this.collapsedEl.show();
39012 this.fireEvent("collapsed", this);
39013 this.fireEvent("invalidated", this);
39019 animateCollapse : function(){
39024 * Expands this region if it was previously collapsed.
39025 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39026 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39029 expand : function(e, skipAnim){
39031 e.stopPropagation();
39033 if(!this.collapsed || this.el.hasActiveFx()) {
39037 this.afterSlideIn();
39040 this.collapsed = false;
39041 if(this.config.animate && skipAnim !== true){
39042 this.animateExpand();
39046 this.split.el.show();
39048 this.collapsedEl.setLocation(-2000,-2000);
39049 this.collapsedEl.hide();
39050 this.fireEvent("invalidated", this);
39051 this.fireEvent("expanded", this);
39055 animateExpand : function(){
39059 initTabs : function()
39061 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39063 var ts = new Roo.bootstrap.panel.Tabs({
39064 el: this.bodyEl.dom,
39066 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39067 disableTooltips: this.config.disableTabTips,
39068 toolbar : this.config.toolbar
39071 if(this.config.hideTabs){
39072 ts.stripWrap.setDisplayed(false);
39075 ts.resizeTabs = this.config.resizeTabs === true;
39076 ts.minTabWidth = this.config.minTabWidth || 40;
39077 ts.maxTabWidth = this.config.maxTabWidth || 250;
39078 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39079 ts.monitorResize = false;
39080 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39081 ts.bodyEl.addClass('roo-layout-tabs-body');
39082 this.panels.each(this.initPanelAsTab, this);
39085 initPanelAsTab : function(panel){
39086 var ti = this.tabs.addTab(
39090 this.config.closeOnTab && panel.isClosable(),
39093 if(panel.tabTip !== undefined){
39094 ti.setTooltip(panel.tabTip);
39096 ti.on("activate", function(){
39097 this.setActivePanel(panel);
39100 if(this.config.closeOnTab){
39101 ti.on("beforeclose", function(t, e){
39103 this.remove(panel);
39107 panel.tabItem = ti;
39112 updatePanelTitle : function(panel, title)
39114 if(this.activePanel == panel){
39115 this.updateTitle(title);
39118 var ti = this.tabs.getTab(panel.getEl().id);
39120 if(panel.tabTip !== undefined){
39121 ti.setTooltip(panel.tabTip);
39126 updateTitle : function(title){
39127 if(this.titleTextEl && !this.config.title){
39128 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39132 setActivePanel : function(panel)
39134 panel = this.getPanel(panel);
39135 if(this.activePanel && this.activePanel != panel){
39136 if(this.activePanel.setActiveState(false) === false){
39140 this.activePanel = panel;
39141 panel.setActiveState(true);
39142 if(this.panelSize){
39143 panel.setSize(this.panelSize.width, this.panelSize.height);
39146 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39148 this.updateTitle(panel.getTitle());
39150 this.fireEvent("invalidated", this);
39152 this.fireEvent("panelactivated", this, panel);
39156 * Shows the specified panel.
39157 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39158 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39160 showPanel : function(panel)
39162 panel = this.getPanel(panel);
39165 var tab = this.tabs.getTab(panel.getEl().id);
39166 if(tab.isHidden()){
39167 this.tabs.unhideTab(tab.id);
39171 this.setActivePanel(panel);
39178 * Get the active panel for this region.
39179 * @return {Roo.ContentPanel} The active panel or null
39181 getActivePanel : function(){
39182 return this.activePanel;
39185 validateVisibility : function(){
39186 if(this.panels.getCount() < 1){
39187 this.updateTitle(" ");
39188 this.closeBtn.hide();
39191 if(!this.isVisible()){
39198 * Adds the passed ContentPanel(s) to this region.
39199 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39200 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39202 add : function(panel)
39204 if(arguments.length > 1){
39205 for(var i = 0, len = arguments.length; i < len; i++) {
39206 this.add(arguments[i]);
39211 // if we have not been rendered yet, then we can not really do much of this..
39212 if (!this.bodyEl) {
39213 this.unrendered_panels.push(panel);
39220 if(this.hasPanel(panel)){
39221 this.showPanel(panel);
39224 panel.setRegion(this);
39225 this.panels.add(panel);
39226 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39227 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39228 // and hide them... ???
39229 this.bodyEl.dom.appendChild(panel.getEl().dom);
39230 if(panel.background !== true){
39231 this.setActivePanel(panel);
39233 this.fireEvent("paneladded", this, panel);
39240 this.initPanelAsTab(panel);
39244 if(panel.background !== true){
39245 this.tabs.activate(panel.getEl().id);
39247 this.fireEvent("paneladded", this, panel);
39252 * Hides the tab for the specified panel.
39253 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39255 hidePanel : function(panel){
39256 if(this.tabs && (panel = this.getPanel(panel))){
39257 this.tabs.hideTab(panel.getEl().id);
39262 * Unhides the tab for a previously hidden panel.
39263 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39265 unhidePanel : function(panel){
39266 if(this.tabs && (panel = this.getPanel(panel))){
39267 this.tabs.unhideTab(panel.getEl().id);
39271 clearPanels : function(){
39272 while(this.panels.getCount() > 0){
39273 this.remove(this.panels.first());
39278 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39279 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39280 * @param {Boolean} preservePanel Overrides the config preservePanel option
39281 * @return {Roo.ContentPanel} The panel that was removed
39283 remove : function(panel, preservePanel)
39285 panel = this.getPanel(panel);
39290 this.fireEvent("beforeremove", this, panel, e);
39291 if(e.cancel === true){
39294 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39295 var panelId = panel.getId();
39296 this.panels.removeKey(panelId);
39298 document.body.appendChild(panel.getEl().dom);
39301 this.tabs.removeTab(panel.getEl().id);
39302 }else if (!preservePanel){
39303 this.bodyEl.dom.removeChild(panel.getEl().dom);
39305 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39306 var p = this.panels.first();
39307 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39308 tempEl.appendChild(p.getEl().dom);
39309 this.bodyEl.update("");
39310 this.bodyEl.dom.appendChild(p.getEl().dom);
39312 this.updateTitle(p.getTitle());
39314 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39315 this.setActivePanel(p);
39317 panel.setRegion(null);
39318 if(this.activePanel == panel){
39319 this.activePanel = null;
39321 if(this.config.autoDestroy !== false && preservePanel !== true){
39322 try{panel.destroy();}catch(e){}
39324 this.fireEvent("panelremoved", this, panel);
39329 * Returns the TabPanel component used by this region
39330 * @return {Roo.TabPanel}
39332 getTabs : function(){
39336 createTool : function(parentEl, className){
39337 var btn = Roo.DomHelper.append(parentEl, {
39339 cls: "x-layout-tools-button",
39342 cls: "roo-layout-tools-button-inner " + className,
39346 btn.addClassOnOver("roo-layout-tools-button-over");
39351 * Ext JS Library 1.1.1
39352 * Copyright(c) 2006-2007, Ext JS, LLC.
39354 * Originally Released Under LGPL - original licence link has changed is not relivant.
39357 * <script type="text/javascript">
39363 * @class Roo.SplitLayoutRegion
39364 * @extends Roo.LayoutRegion
39365 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39367 Roo.bootstrap.layout.Split = function(config){
39368 this.cursor = config.cursor;
39369 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39372 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39374 splitTip : "Drag to resize.",
39375 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39376 useSplitTips : false,
39378 applyConfig : function(config){
39379 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39382 onRender : function(ctr,pos) {
39384 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39385 if(!this.config.split){
39390 var splitEl = Roo.DomHelper.append(ctr.dom, {
39392 id: this.el.id + "-split",
39393 cls: "roo-layout-split roo-layout-split-"+this.position,
39396 /** The SplitBar for this region
39397 * @type Roo.SplitBar */
39398 // does not exist yet...
39399 Roo.log([this.position, this.orientation]);
39401 this.split = new Roo.bootstrap.SplitBar({
39402 dragElement : splitEl,
39403 resizingElement: this.el,
39404 orientation : this.orientation
39407 this.split.on("moved", this.onSplitMove, this);
39408 this.split.useShim = this.config.useShim === true;
39409 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39410 if(this.useSplitTips){
39411 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39413 //if(config.collapsible){
39414 // this.split.el.on("dblclick", this.collapse, this);
39417 if(typeof this.config.minSize != "undefined"){
39418 this.split.minSize = this.config.minSize;
39420 if(typeof this.config.maxSize != "undefined"){
39421 this.split.maxSize = this.config.maxSize;
39423 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39424 this.hideSplitter();
39429 getHMaxSize : function(){
39430 var cmax = this.config.maxSize || 10000;
39431 var center = this.mgr.getRegion("center");
39432 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39435 getVMaxSize : function(){
39436 var cmax = this.config.maxSize || 10000;
39437 var center = this.mgr.getRegion("center");
39438 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39441 onSplitMove : function(split, newSize){
39442 this.fireEvent("resized", this, newSize);
39446 * Returns the {@link Roo.SplitBar} for this region.
39447 * @return {Roo.SplitBar}
39449 getSplitBar : function(){
39454 this.hideSplitter();
39455 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39458 hideSplitter : function(){
39460 this.split.el.setLocation(-2000,-2000);
39461 this.split.el.hide();
39467 this.split.el.show();
39469 Roo.bootstrap.layout.Split.superclass.show.call(this);
39472 beforeSlide: function(){
39473 if(Roo.isGecko){// firefox overflow auto bug workaround
39474 this.bodyEl.clip();
39476 this.tabs.bodyEl.clip();
39478 if(this.activePanel){
39479 this.activePanel.getEl().clip();
39481 if(this.activePanel.beforeSlide){
39482 this.activePanel.beforeSlide();
39488 afterSlide : function(){
39489 if(Roo.isGecko){// firefox overflow auto bug workaround
39490 this.bodyEl.unclip();
39492 this.tabs.bodyEl.unclip();
39494 if(this.activePanel){
39495 this.activePanel.getEl().unclip();
39496 if(this.activePanel.afterSlide){
39497 this.activePanel.afterSlide();
39503 initAutoHide : function(){
39504 if(this.autoHide !== false){
39505 if(!this.autoHideHd){
39506 var st = new Roo.util.DelayedTask(this.slideIn, this);
39507 this.autoHideHd = {
39508 "mouseout": function(e){
39509 if(!e.within(this.el, true)){
39513 "mouseover" : function(e){
39519 this.el.on(this.autoHideHd);
39523 clearAutoHide : function(){
39524 if(this.autoHide !== false){
39525 this.el.un("mouseout", this.autoHideHd.mouseout);
39526 this.el.un("mouseover", this.autoHideHd.mouseover);
39530 clearMonitor : function(){
39531 Roo.get(document).un("click", this.slideInIf, this);
39534 // these names are backwards but not changed for compat
39535 slideOut : function(){
39536 if(this.isSlid || this.el.hasActiveFx()){
39539 this.isSlid = true;
39540 if(this.collapseBtn){
39541 this.collapseBtn.hide();
39543 this.closeBtnState = this.closeBtn.getStyle('display');
39544 this.closeBtn.hide();
39546 this.stickBtn.show();
39549 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39550 this.beforeSlide();
39551 this.el.setStyle("z-index", 10001);
39552 this.el.slideIn(this.getSlideAnchor(), {
39553 callback: function(){
39555 this.initAutoHide();
39556 Roo.get(document).on("click", this.slideInIf, this);
39557 this.fireEvent("slideshow", this);
39564 afterSlideIn : function(){
39565 this.clearAutoHide();
39566 this.isSlid = false;
39567 this.clearMonitor();
39568 this.el.setStyle("z-index", "");
39569 if(this.collapseBtn){
39570 this.collapseBtn.show();
39572 this.closeBtn.setStyle('display', this.closeBtnState);
39574 this.stickBtn.hide();
39576 this.fireEvent("slidehide", this);
39579 slideIn : function(cb){
39580 if(!this.isSlid || this.el.hasActiveFx()){
39584 this.isSlid = false;
39585 this.beforeSlide();
39586 this.el.slideOut(this.getSlideAnchor(), {
39587 callback: function(){
39588 this.el.setLeftTop(-10000, -10000);
39590 this.afterSlideIn();
39598 slideInIf : function(e){
39599 if(!e.within(this.el)){
39604 animateCollapse : function(){
39605 this.beforeSlide();
39606 this.el.setStyle("z-index", 20000);
39607 var anchor = this.getSlideAnchor();
39608 this.el.slideOut(anchor, {
39609 callback : function(){
39610 this.el.setStyle("z-index", "");
39611 this.collapsedEl.slideIn(anchor, {duration:.3});
39613 this.el.setLocation(-10000,-10000);
39615 this.fireEvent("collapsed", this);
39622 animateExpand : function(){
39623 this.beforeSlide();
39624 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39625 this.el.setStyle("z-index", 20000);
39626 this.collapsedEl.hide({
39629 this.el.slideIn(this.getSlideAnchor(), {
39630 callback : function(){
39631 this.el.setStyle("z-index", "");
39634 this.split.el.show();
39636 this.fireEvent("invalidated", this);
39637 this.fireEvent("expanded", this);
39665 getAnchor : function(){
39666 return this.anchors[this.position];
39669 getCollapseAnchor : function(){
39670 return this.canchors[this.position];
39673 getSlideAnchor : function(){
39674 return this.sanchors[this.position];
39677 getAlignAdj : function(){
39678 var cm = this.cmargins;
39679 switch(this.position){
39695 getExpandAdj : function(){
39696 var c = this.collapsedEl, cm = this.cmargins;
39697 switch(this.position){
39699 return [-(cm.right+c.getWidth()+cm.left), 0];
39702 return [cm.right+c.getWidth()+cm.left, 0];
39705 return [0, -(cm.top+cm.bottom+c.getHeight())];
39708 return [0, cm.top+cm.bottom+c.getHeight()];
39714 * Ext JS Library 1.1.1
39715 * Copyright(c) 2006-2007, Ext JS, LLC.
39717 * Originally Released Under LGPL - original licence link has changed is not relivant.
39720 * <script type="text/javascript">
39723 * These classes are private internal classes
39725 Roo.bootstrap.layout.Center = function(config){
39726 config.region = "center";
39727 Roo.bootstrap.layout.Region.call(this, config);
39728 this.visible = true;
39729 this.minWidth = config.minWidth || 20;
39730 this.minHeight = config.minHeight || 20;
39733 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39735 // center panel can't be hidden
39739 // center panel can't be hidden
39742 getMinWidth: function(){
39743 return this.minWidth;
39746 getMinHeight: function(){
39747 return this.minHeight;
39761 Roo.bootstrap.layout.North = function(config)
39763 config.region = 'north';
39764 config.cursor = 'n-resize';
39766 Roo.bootstrap.layout.Split.call(this, config);
39770 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39771 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39772 this.split.el.addClass("roo-layout-split-v");
39774 //var size = config.initialSize || config.height;
39775 //if(this.el && typeof size != "undefined"){
39776 // this.el.setHeight(size);
39779 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39781 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39784 onRender : function(ctr, pos)
39786 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39787 var size = this.config.initialSize || this.config.height;
39788 if(this.el && typeof size != "undefined"){
39789 this.el.setHeight(size);
39794 getBox : function(){
39795 if(this.collapsed){
39796 return this.collapsedEl.getBox();
39798 var box = this.el.getBox();
39800 box.height += this.split.el.getHeight();
39805 updateBox : function(box){
39806 if(this.split && !this.collapsed){
39807 box.height -= this.split.el.getHeight();
39808 this.split.el.setLeft(box.x);
39809 this.split.el.setTop(box.y+box.height);
39810 this.split.el.setWidth(box.width);
39812 if(this.collapsed){
39813 this.updateBody(box.width, null);
39815 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39823 Roo.bootstrap.layout.South = function(config){
39824 config.region = 'south';
39825 config.cursor = 's-resize';
39826 Roo.bootstrap.layout.Split.call(this, config);
39828 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39829 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39830 this.split.el.addClass("roo-layout-split-v");
39835 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39836 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39838 onRender : function(ctr, pos)
39840 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39841 var size = this.config.initialSize || this.config.height;
39842 if(this.el && typeof size != "undefined"){
39843 this.el.setHeight(size);
39848 getBox : function(){
39849 if(this.collapsed){
39850 return this.collapsedEl.getBox();
39852 var box = this.el.getBox();
39854 var sh = this.split.el.getHeight();
39861 updateBox : function(box){
39862 if(this.split && !this.collapsed){
39863 var sh = this.split.el.getHeight();
39866 this.split.el.setLeft(box.x);
39867 this.split.el.setTop(box.y-sh);
39868 this.split.el.setWidth(box.width);
39870 if(this.collapsed){
39871 this.updateBody(box.width, null);
39873 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39877 Roo.bootstrap.layout.East = function(config){
39878 config.region = "east";
39879 config.cursor = "e-resize";
39880 Roo.bootstrap.layout.Split.call(this, config);
39882 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39883 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39884 this.split.el.addClass("roo-layout-split-h");
39888 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39889 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39891 onRender : function(ctr, pos)
39893 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39894 var size = this.config.initialSize || this.config.width;
39895 if(this.el && typeof size != "undefined"){
39896 this.el.setWidth(size);
39901 getBox : function(){
39902 if(this.collapsed){
39903 return this.collapsedEl.getBox();
39905 var box = this.el.getBox();
39907 var sw = this.split.el.getWidth();
39914 updateBox : function(box){
39915 if(this.split && !this.collapsed){
39916 var sw = this.split.el.getWidth();
39918 this.split.el.setLeft(box.x);
39919 this.split.el.setTop(box.y);
39920 this.split.el.setHeight(box.height);
39923 if(this.collapsed){
39924 this.updateBody(null, box.height);
39926 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39930 Roo.bootstrap.layout.West = function(config){
39931 config.region = "west";
39932 config.cursor = "w-resize";
39934 Roo.bootstrap.layout.Split.call(this, config);
39936 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39937 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39938 this.split.el.addClass("roo-layout-split-h");
39942 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39943 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39945 onRender: function(ctr, pos)
39947 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39948 var size = this.config.initialSize || this.config.width;
39949 if(typeof size != "undefined"){
39950 this.el.setWidth(size);
39954 getBox : function(){
39955 if(this.collapsed){
39956 return this.collapsedEl.getBox();
39958 var box = this.el.getBox();
39959 if (box.width == 0) {
39960 box.width = this.config.width; // kludge?
39963 box.width += this.split.el.getWidth();
39968 updateBox : function(box){
39969 if(this.split && !this.collapsed){
39970 var sw = this.split.el.getWidth();
39972 this.split.el.setLeft(box.x+box.width);
39973 this.split.el.setTop(box.y);
39974 this.split.el.setHeight(box.height);
39976 if(this.collapsed){
39977 this.updateBody(null, box.height);
39979 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39981 });Roo.namespace("Roo.bootstrap.panel");/*
39983 * Ext JS Library 1.1.1
39984 * Copyright(c) 2006-2007, Ext JS, LLC.
39986 * Originally Released Under LGPL - original licence link has changed is not relivant.
39989 * <script type="text/javascript">
39992 * @class Roo.ContentPanel
39993 * @extends Roo.util.Observable
39994 * A basic ContentPanel element.
39995 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39996 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39997 * @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
39998 * @cfg {Boolean} closable True if the panel can be closed/removed
39999 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40000 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40001 * @cfg {Toolbar} toolbar A toolbar for this panel
40002 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40003 * @cfg {String} title The title for this panel
40004 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40005 * @cfg {String} url Calls {@link #setUrl} with this value
40006 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40007 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40008 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40009 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40010 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40011 * @cfg {Boolean} badges render the badges
40012 * @cfg {String} cls extra classes to use
40013 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40016 * Create a new ContentPanel.
40017 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40018 * @param {String/Object} config A string to set only the title or a config object
40019 * @param {String} content (optional) Set the HTML content for this panel
40020 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40022 Roo.bootstrap.panel.Content = function( config){
40024 this.tpl = config.tpl || false;
40026 var el = config.el;
40027 var content = config.content;
40029 if(config.autoCreate){ // xtype is available if this is called from factory
40032 this.el = Roo.get(el);
40033 if(!this.el && config && config.autoCreate){
40034 if(typeof config.autoCreate == "object"){
40035 if(!config.autoCreate.id){
40036 config.autoCreate.id = config.id||el;
40038 this.el = Roo.DomHelper.append(document.body,
40039 config.autoCreate, true);
40043 cls: (config.cls || '') +
40044 (config.background ? ' bg-' + config.background : '') +
40045 " roo-layout-inactive-content",
40048 if (config.iframe) {
40052 style : 'border: 0px',
40053 src : 'about:blank'
40059 elcfg.html = config.html;
40063 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40064 if (config.iframe) {
40065 this.iframeEl = this.el.select('iframe',true).first();
40070 this.closable = false;
40071 this.loaded = false;
40072 this.active = false;
40075 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40077 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40079 this.wrapEl = this.el; //this.el.wrap();
40081 if (config.toolbar.items) {
40082 ti = config.toolbar.items ;
40083 delete config.toolbar.items ;
40087 this.toolbar.render(this.wrapEl, 'before');
40088 for(var i =0;i < ti.length;i++) {
40089 // Roo.log(['add child', items[i]]);
40090 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40092 this.toolbar.items = nitems;
40093 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40094 delete config.toolbar;
40098 // xtype created footer. - not sure if will work as we normally have to render first..
40099 if (this.footer && !this.footer.el && this.footer.xtype) {
40100 if (!this.wrapEl) {
40101 this.wrapEl = this.el.wrap();
40104 this.footer.container = this.wrapEl.createChild();
40106 this.footer = Roo.factory(this.footer, Roo);
40111 if(typeof config == "string"){
40112 this.title = config;
40114 Roo.apply(this, config);
40118 this.resizeEl = Roo.get(this.resizeEl, true);
40120 this.resizeEl = this.el;
40122 // handle view.xtype
40130 * Fires when this panel is activated.
40131 * @param {Roo.ContentPanel} this
40135 * @event deactivate
40136 * Fires when this panel is activated.
40137 * @param {Roo.ContentPanel} this
40139 "deactivate" : true,
40143 * Fires when this panel is resized if fitToFrame is true.
40144 * @param {Roo.ContentPanel} this
40145 * @param {Number} width The width after any component adjustments
40146 * @param {Number} height The height after any component adjustments
40152 * Fires when this tab is created
40153 * @param {Roo.ContentPanel} this
40164 if(this.autoScroll && !this.iframe){
40165 this.resizeEl.setStyle("overflow", "auto");
40167 // fix randome scrolling
40168 //this.el.on('scroll', function() {
40169 // Roo.log('fix random scolling');
40170 // this.scrollTo('top',0);
40173 content = content || this.content;
40175 this.setContent(content);
40177 if(config && config.url){
40178 this.setUrl(this.url, this.params, this.loadOnce);
40183 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40185 if (this.view && typeof(this.view.xtype) != 'undefined') {
40186 this.view.el = this.el.appendChild(document.createElement("div"));
40187 this.view = Roo.factory(this.view);
40188 this.view.render && this.view.render(false, '');
40192 this.fireEvent('render', this);
40195 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40205 setRegion : function(region){
40206 this.region = region;
40207 this.setActiveClass(region && !this.background);
40211 setActiveClass: function(state)
40214 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40215 this.el.setStyle('position','relative');
40217 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40218 this.el.setStyle('position', 'absolute');
40223 * Returns the toolbar for this Panel if one was configured.
40224 * @return {Roo.Toolbar}
40226 getToolbar : function(){
40227 return this.toolbar;
40230 setActiveState : function(active)
40232 this.active = active;
40233 this.setActiveClass(active);
40235 if(this.fireEvent("deactivate", this) === false){
40240 this.fireEvent("activate", this);
40244 * Updates this panel's element (not for iframe)
40245 * @param {String} content The new content
40246 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40248 setContent : function(content, loadScripts){
40253 this.el.update(content, loadScripts);
40256 ignoreResize : function(w, h){
40257 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40260 this.lastSize = {width: w, height: h};
40265 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40266 * @return {Roo.UpdateManager} The UpdateManager
40268 getUpdateManager : function(){
40272 return this.el.getUpdateManager();
40275 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40276 * Does not work with IFRAME contents
40277 * @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:
40280 url: "your-url.php",
40281 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40282 callback: yourFunction,
40283 scope: yourObject, //(optional scope)
40286 text: "Loading...",
40292 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40293 * 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.
40294 * @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}
40295 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40296 * @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.
40297 * @return {Roo.ContentPanel} this
40305 var um = this.el.getUpdateManager();
40306 um.update.apply(um, arguments);
40312 * 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.
40313 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40314 * @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)
40315 * @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)
40316 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40318 setUrl : function(url, params, loadOnce){
40320 this.iframeEl.dom.src = url;
40324 if(this.refreshDelegate){
40325 this.removeListener("activate", this.refreshDelegate);
40327 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40328 this.on("activate", this.refreshDelegate);
40329 return this.el.getUpdateManager();
40332 _handleRefresh : function(url, params, loadOnce){
40333 if(!loadOnce || !this.loaded){
40334 var updater = this.el.getUpdateManager();
40335 updater.update(url, params, this._setLoaded.createDelegate(this));
40339 _setLoaded : function(){
40340 this.loaded = true;
40344 * Returns this panel's id
40347 getId : function(){
40352 * Returns this panel's element - used by regiosn to add.
40353 * @return {Roo.Element}
40355 getEl : function(){
40356 return this.wrapEl || this.el;
40361 adjustForComponents : function(width, height)
40363 //Roo.log('adjustForComponents ');
40364 if(this.resizeEl != this.el){
40365 width -= this.el.getFrameWidth('lr');
40366 height -= this.el.getFrameWidth('tb');
40369 var te = this.toolbar.getEl();
40370 te.setWidth(width);
40371 height -= te.getHeight();
40374 var te = this.footer.getEl();
40375 te.setWidth(width);
40376 height -= te.getHeight();
40380 if(this.adjustments){
40381 width += this.adjustments[0];
40382 height += this.adjustments[1];
40384 return {"width": width, "height": height};
40387 setSize : function(width, height){
40388 if(this.fitToFrame && !this.ignoreResize(width, height)){
40389 if(this.fitContainer && this.resizeEl != this.el){
40390 this.el.setSize(width, height);
40392 var size = this.adjustForComponents(width, height);
40394 this.iframeEl.setSize(width,height);
40397 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40398 this.fireEvent('resize', this, size.width, size.height);
40405 * Returns this panel's title
40408 getTitle : function(){
40410 if (typeof(this.title) != 'object') {
40415 for (var k in this.title) {
40416 if (!this.title.hasOwnProperty(k)) {
40420 if (k.indexOf('-') >= 0) {
40421 var s = k.split('-');
40422 for (var i = 0; i<s.length; i++) {
40423 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40426 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40433 * Set this panel's title
40434 * @param {String} title
40436 setTitle : function(title){
40437 this.title = title;
40439 this.region.updatePanelTitle(this, title);
40444 * Returns true is this panel was configured to be closable
40445 * @return {Boolean}
40447 isClosable : function(){
40448 return this.closable;
40451 beforeSlide : function(){
40453 this.resizeEl.clip();
40456 afterSlide : function(){
40458 this.resizeEl.unclip();
40462 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40463 * Will fail silently if the {@link #setUrl} method has not been called.
40464 * This does not activate the panel, just updates its content.
40466 refresh : function(){
40467 if(this.refreshDelegate){
40468 this.loaded = false;
40469 this.refreshDelegate();
40474 * Destroys this panel
40476 destroy : function(){
40477 this.el.removeAllListeners();
40478 var tempEl = document.createElement("span");
40479 tempEl.appendChild(this.el.dom);
40480 tempEl.innerHTML = "";
40486 * form - if the content panel contains a form - this is a reference to it.
40487 * @type {Roo.form.Form}
40491 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40492 * This contains a reference to it.
40498 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40508 * @param {Object} cfg Xtype definition of item to add.
40512 getChildContainer: function () {
40513 return this.getEl();
40518 var ret = new Roo.factory(cfg);
40523 if (cfg.xtype.match(/^Form$/)) {
40526 //if (this.footer) {
40527 // el = this.footer.container.insertSibling(false, 'before');
40529 el = this.el.createChild();
40532 this.form = new Roo.form.Form(cfg);
40535 if ( this.form.allItems.length) {
40536 this.form.render(el.dom);
40540 // should only have one of theses..
40541 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40542 // views.. should not be just added - used named prop 'view''
40544 cfg.el = this.el.appendChild(document.createElement("div"));
40547 var ret = new Roo.factory(cfg);
40549 ret.render && ret.render(false, ''); // render blank..
40559 * @class Roo.bootstrap.panel.Grid
40560 * @extends Roo.bootstrap.panel.Content
40562 * Create a new GridPanel.
40563 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40564 * @param {Object} config A the config object
40570 Roo.bootstrap.panel.Grid = function(config)
40574 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40575 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40577 config.el = this.wrapper;
40578 //this.el = this.wrapper;
40580 if (config.container) {
40581 // ctor'ed from a Border/panel.grid
40584 this.wrapper.setStyle("overflow", "hidden");
40585 this.wrapper.addClass('roo-grid-container');
40590 if(config.toolbar){
40591 var tool_el = this.wrapper.createChild();
40592 this.toolbar = Roo.factory(config.toolbar);
40594 if (config.toolbar.items) {
40595 ti = config.toolbar.items ;
40596 delete config.toolbar.items ;
40600 this.toolbar.render(tool_el);
40601 for(var i =0;i < ti.length;i++) {
40602 // Roo.log(['add child', items[i]]);
40603 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40605 this.toolbar.items = nitems;
40607 delete config.toolbar;
40610 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40611 config.grid.scrollBody = true;;
40612 config.grid.monitorWindowResize = false; // turn off autosizing
40613 config.grid.autoHeight = false;
40614 config.grid.autoWidth = false;
40616 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40618 if (config.background) {
40619 // render grid on panel activation (if panel background)
40620 this.on('activate', function(gp) {
40621 if (!gp.grid.rendered) {
40622 gp.grid.render(this.wrapper);
40623 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40628 this.grid.render(this.wrapper);
40629 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40632 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40633 // ??? needed ??? config.el = this.wrapper;
40638 // xtype created footer. - not sure if will work as we normally have to render first..
40639 if (this.footer && !this.footer.el && this.footer.xtype) {
40641 var ctr = this.grid.getView().getFooterPanel(true);
40642 this.footer.dataSource = this.grid.dataSource;
40643 this.footer = Roo.factory(this.footer, Roo);
40644 this.footer.render(ctr);
40654 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40655 getId : function(){
40656 return this.grid.id;
40660 * Returns the grid for this panel
40661 * @return {Roo.bootstrap.Table}
40663 getGrid : function(){
40667 setSize : function(width, height){
40668 if(!this.ignoreResize(width, height)){
40669 var grid = this.grid;
40670 var size = this.adjustForComponents(width, height);
40671 // tfoot is not a footer?
40674 var gridel = grid.getGridEl();
40675 gridel.setSize(size.width, size.height);
40677 var tbd = grid.getGridEl().select('tbody', true).first();
40678 var thd = grid.getGridEl().select('thead',true).first();
40679 var tbf= grid.getGridEl().select('tfoot', true).first();
40682 size.height -= tbf.getHeight();
40685 size.height -= thd.getHeight();
40688 tbd.setSize(size.width, size.height );
40689 // this is for the account management tab -seems to work there.
40690 var thd = grid.getGridEl().select('thead',true).first();
40692 // tbd.setSize(size.width, size.height - thd.getHeight());
40701 beforeSlide : function(){
40702 this.grid.getView().scroller.clip();
40705 afterSlide : function(){
40706 this.grid.getView().scroller.unclip();
40709 destroy : function(){
40710 this.grid.destroy();
40712 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40717 * @class Roo.bootstrap.panel.Nest
40718 * @extends Roo.bootstrap.panel.Content
40720 * Create a new Panel, that can contain a layout.Border.
40723 * @param {Roo.BorderLayout} layout The layout for this panel
40724 * @param {String/Object} config A string to set only the title or a config object
40726 Roo.bootstrap.panel.Nest = function(config)
40728 // construct with only one argument..
40729 /* FIXME - implement nicer consturctors
40730 if (layout.layout) {
40732 layout = config.layout;
40733 delete config.layout;
40735 if (layout.xtype && !layout.getEl) {
40736 // then layout needs constructing..
40737 layout = Roo.factory(layout, Roo);
40741 config.el = config.layout.getEl();
40743 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40745 config.layout.monitorWindowResize = false; // turn off autosizing
40746 this.layout = config.layout;
40747 this.layout.getEl().addClass("roo-layout-nested-layout");
40748 this.layout.parent = this;
40755 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40757 setSize : function(width, height){
40758 if(!this.ignoreResize(width, height)){
40759 var size = this.adjustForComponents(width, height);
40760 var el = this.layout.getEl();
40761 if (size.height < 1) {
40762 el.setWidth(size.width);
40764 el.setSize(size.width, size.height);
40766 var touch = el.dom.offsetWidth;
40767 this.layout.layout();
40768 // ie requires a double layout on the first pass
40769 if(Roo.isIE && !this.initialized){
40770 this.initialized = true;
40771 this.layout.layout();
40776 // activate all subpanels if not currently active..
40778 setActiveState : function(active){
40779 this.active = active;
40780 this.setActiveClass(active);
40783 this.fireEvent("deactivate", this);
40787 this.fireEvent("activate", this);
40788 // not sure if this should happen before or after..
40789 if (!this.layout) {
40790 return; // should not happen..
40793 for (var r in this.layout.regions) {
40794 reg = this.layout.getRegion(r);
40795 if (reg.getActivePanel()) {
40796 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40797 reg.setActivePanel(reg.getActivePanel());
40800 if (!reg.panels.length) {
40803 reg.showPanel(reg.getPanel(0));
40812 * Returns the nested BorderLayout for this panel
40813 * @return {Roo.BorderLayout}
40815 getLayout : function(){
40816 return this.layout;
40820 * Adds a xtype elements to the layout of the nested panel
40824 xtype : 'ContentPanel',
40831 xtype : 'NestedLayoutPanel',
40837 items : [ ... list of content panels or nested layout panels.. ]
40841 * @param {Object} cfg Xtype definition of item to add.
40843 addxtype : function(cfg) {
40844 return this.layout.addxtype(cfg);
40849 * Ext JS Library 1.1.1
40850 * Copyright(c) 2006-2007, Ext JS, LLC.
40852 * Originally Released Under LGPL - original licence link has changed is not relivant.
40855 * <script type="text/javascript">
40858 * @class Roo.TabPanel
40859 * @extends Roo.util.Observable
40860 * A lightweight tab container.
40864 // basic tabs 1, built from existing content
40865 var tabs = new Roo.TabPanel("tabs1");
40866 tabs.addTab("script", "View Script");
40867 tabs.addTab("markup", "View Markup");
40868 tabs.activate("script");
40870 // more advanced tabs, built from javascript
40871 var jtabs = new Roo.TabPanel("jtabs");
40872 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40874 // set up the UpdateManager
40875 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40876 var updater = tab2.getUpdateManager();
40877 updater.setDefaultUrl("ajax1.htm");
40878 tab2.on('activate', updater.refresh, updater, true);
40880 // Use setUrl for Ajax loading
40881 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40882 tab3.setUrl("ajax2.htm", null, true);
40885 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40888 jtabs.activate("jtabs-1");
40891 * Create a new TabPanel.
40892 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40893 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40895 Roo.bootstrap.panel.Tabs = function(config){
40897 * The container element for this TabPanel.
40898 * @type Roo.Element
40900 this.el = Roo.get(config.el);
40903 if(typeof config == "boolean"){
40904 this.tabPosition = config ? "bottom" : "top";
40906 Roo.apply(this, config);
40910 if(this.tabPosition == "bottom"){
40911 // if tabs are at the bottom = create the body first.
40912 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40913 this.el.addClass("roo-tabs-bottom");
40915 // next create the tabs holders
40917 if (this.tabPosition == "west"){
40919 var reg = this.region; // fake it..
40921 if (!reg.mgr.parent) {
40924 reg = reg.mgr.parent.region;
40926 Roo.log("got nest?");
40928 if (reg.mgr.getRegion('west')) {
40929 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40930 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40931 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40932 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40933 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40941 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40942 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40943 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40944 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40949 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40952 // finally - if tabs are at the top, then create the body last..
40953 if(this.tabPosition != "bottom"){
40954 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40955 * @type Roo.Element
40957 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40958 this.el.addClass("roo-tabs-top");
40962 this.bodyEl.setStyle("position", "relative");
40964 this.active = null;
40965 this.activateDelegate = this.activate.createDelegate(this);
40970 * Fires when the active tab changes
40971 * @param {Roo.TabPanel} this
40972 * @param {Roo.TabPanelItem} activePanel The new active tab
40976 * @event beforetabchange
40977 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40978 * @param {Roo.TabPanel} this
40979 * @param {Object} e Set cancel to true on this object to cancel the tab change
40980 * @param {Roo.TabPanelItem} tab The tab being changed to
40982 "beforetabchange" : true
40985 Roo.EventManager.onWindowResize(this.onResize, this);
40986 this.cpad = this.el.getPadding("lr");
40987 this.hiddenCount = 0;
40990 // toolbar on the tabbar support...
40991 if (this.toolbar) {
40992 alert("no toolbar support yet");
40993 this.toolbar = false;
40995 var tcfg = this.toolbar;
40996 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40997 this.toolbar = new Roo.Toolbar(tcfg);
40998 if (Roo.isSafari) {
40999 var tbl = tcfg.container.child('table', true);
41000 tbl.setAttribute('width', '100%');
41008 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41011 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41013 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41015 tabPosition : "top",
41017 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41019 currentTabWidth : 0,
41021 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41025 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41029 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41031 preferredTabWidth : 175,
41033 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41035 resizeTabs : false,
41037 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41039 monitorResize : true,
41041 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41043 toolbar : false, // set by caller..
41045 region : false, /// set by caller
41047 disableTooltips : true, // not used yet...
41050 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41051 * @param {String} id The id of the div to use <b>or create</b>
41052 * @param {String} text The text for the tab
41053 * @param {String} content (optional) Content to put in the TabPanelItem body
41054 * @param {Boolean} closable (optional) True to create a close icon on the tab
41055 * @return {Roo.TabPanelItem} The created TabPanelItem
41057 addTab : function(id, text, content, closable, tpl)
41059 var item = new Roo.bootstrap.panel.TabItem({
41063 closable : closable,
41066 this.addTabItem(item);
41068 item.setContent(content);
41074 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41075 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41076 * @return {Roo.TabPanelItem}
41078 getTab : function(id){
41079 return this.items[id];
41083 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41084 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41086 hideTab : function(id){
41087 var t = this.items[id];
41090 this.hiddenCount++;
41091 this.autoSizeTabs();
41096 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41097 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41099 unhideTab : function(id){
41100 var t = this.items[id];
41102 t.setHidden(false);
41103 this.hiddenCount--;
41104 this.autoSizeTabs();
41109 * Adds an existing {@link Roo.TabPanelItem}.
41110 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41112 addTabItem : function(item)
41114 this.items[item.id] = item;
41115 this.items.push(item);
41116 this.autoSizeTabs();
41117 // if(this.resizeTabs){
41118 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41119 // this.autoSizeTabs();
41121 // item.autoSize();
41126 * Removes a {@link Roo.TabPanelItem}.
41127 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41129 removeTab : function(id){
41130 var items = this.items;
41131 var tab = items[id];
41132 if(!tab) { return; }
41133 var index = items.indexOf(tab);
41134 if(this.active == tab && items.length > 1){
41135 var newTab = this.getNextAvailable(index);
41140 this.stripEl.dom.removeChild(tab.pnode.dom);
41141 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41142 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41144 items.splice(index, 1);
41145 delete this.items[tab.id];
41146 tab.fireEvent("close", tab);
41147 tab.purgeListeners();
41148 this.autoSizeTabs();
41151 getNextAvailable : function(start){
41152 var items = this.items;
41154 // look for a next tab that will slide over to
41155 // replace the one being removed
41156 while(index < items.length){
41157 var item = items[++index];
41158 if(item && !item.isHidden()){
41162 // if one isn't found select the previous tab (on the left)
41165 var item = items[--index];
41166 if(item && !item.isHidden()){
41174 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41175 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41177 disableTab : function(id){
41178 var tab = this.items[id];
41179 if(tab && this.active != tab){
41185 * Enables a {@link Roo.TabPanelItem} that is disabled.
41186 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41188 enableTab : function(id){
41189 var tab = this.items[id];
41194 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41195 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41196 * @return {Roo.TabPanelItem} The TabPanelItem.
41198 activate : function(id)
41200 //Roo.log('activite:' + id);
41202 var tab = this.items[id];
41206 if(tab == this.active || tab.disabled){
41210 this.fireEvent("beforetabchange", this, e, tab);
41211 if(e.cancel !== true && !tab.disabled){
41213 this.active.hide();
41215 this.active = this.items[id];
41216 this.active.show();
41217 this.fireEvent("tabchange", this, this.active);
41223 * Gets the active {@link Roo.TabPanelItem}.
41224 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41226 getActiveTab : function(){
41227 return this.active;
41231 * Updates the tab body element to fit the height of the container element
41232 * for overflow scrolling
41233 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41235 syncHeight : function(targetHeight){
41236 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41237 var bm = this.bodyEl.getMargins();
41238 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41239 this.bodyEl.setHeight(newHeight);
41243 onResize : function(){
41244 if(this.monitorResize){
41245 this.autoSizeTabs();
41250 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41252 beginUpdate : function(){
41253 this.updating = true;
41257 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41259 endUpdate : function(){
41260 this.updating = false;
41261 this.autoSizeTabs();
41265 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41267 autoSizeTabs : function()
41269 var count = this.items.length;
41270 var vcount = count - this.hiddenCount;
41273 this.stripEl.hide();
41275 this.stripEl.show();
41278 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41283 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41284 var availWidth = Math.floor(w / vcount);
41285 var b = this.stripBody;
41286 if(b.getWidth() > w){
41287 var tabs = this.items;
41288 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41289 if(availWidth < this.minTabWidth){
41290 /*if(!this.sleft){ // incomplete scrolling code
41291 this.createScrollButtons();
41294 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41297 if(this.currentTabWidth < this.preferredTabWidth){
41298 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41304 * Returns the number of tabs in this TabPanel.
41307 getCount : function(){
41308 return this.items.length;
41312 * Resizes all the tabs to the passed width
41313 * @param {Number} The new width
41315 setTabWidth : function(width){
41316 this.currentTabWidth = width;
41317 for(var i = 0, len = this.items.length; i < len; i++) {
41318 if(!this.items[i].isHidden()) {
41319 this.items[i].setWidth(width);
41325 * Destroys this TabPanel
41326 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41328 destroy : function(removeEl){
41329 Roo.EventManager.removeResizeListener(this.onResize, this);
41330 for(var i = 0, len = this.items.length; i < len; i++){
41331 this.items[i].purgeListeners();
41333 if(removeEl === true){
41334 this.el.update("");
41339 createStrip : function(container)
41341 var strip = document.createElement("nav");
41342 strip.className = Roo.bootstrap.version == 4 ?
41343 "navbar-light bg-light" :
41344 "navbar navbar-default"; //"x-tabs-wrap";
41345 container.appendChild(strip);
41349 createStripList : function(strip)
41351 // div wrapper for retard IE
41352 // returns the "tr" element.
41353 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41354 //'<div class="x-tabs-strip-wrap">'+
41355 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41356 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41357 return strip.firstChild; //.firstChild.firstChild.firstChild;
41359 createBody : function(container)
41361 var body = document.createElement("div");
41362 Roo.id(body, "tab-body");
41363 //Roo.fly(body).addClass("x-tabs-body");
41364 Roo.fly(body).addClass("tab-content");
41365 container.appendChild(body);
41368 createItemBody :function(bodyEl, id){
41369 var body = Roo.getDom(id);
41371 body = document.createElement("div");
41374 //Roo.fly(body).addClass("x-tabs-item-body");
41375 Roo.fly(body).addClass("tab-pane");
41376 bodyEl.insertBefore(body, bodyEl.firstChild);
41380 createStripElements : function(stripEl, text, closable, tpl)
41382 var td = document.createElement("li"); // was td..
41383 td.className = 'nav-item';
41385 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41388 stripEl.appendChild(td);
41390 td.className = "x-tabs-closable";
41391 if(!this.closeTpl){
41392 this.closeTpl = new Roo.Template(
41393 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41394 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41395 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41398 var el = this.closeTpl.overwrite(td, {"text": text});
41399 var close = el.getElementsByTagName("div")[0];
41400 var inner = el.getElementsByTagName("em")[0];
41401 return {"el": el, "close": close, "inner": inner};
41404 // not sure what this is..
41405 // if(!this.tabTpl){
41406 //this.tabTpl = new Roo.Template(
41407 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41408 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41410 // this.tabTpl = new Roo.Template(
41411 // '<a href="#">' +
41412 // '<span unselectable="on"' +
41413 // (this.disableTooltips ? '' : ' title="{text}"') +
41414 // ' >{text}</span></a>'
41420 var template = tpl || this.tabTpl || false;
41423 template = new Roo.Template(
41424 Roo.bootstrap.version == 4 ?
41426 '<a class="nav-link" href="#" unselectable="on"' +
41427 (this.disableTooltips ? '' : ' title="{text}"') +
41430 '<a class="nav-link" href="#">' +
41431 '<span unselectable="on"' +
41432 (this.disableTooltips ? '' : ' title="{text}"') +
41433 ' >{text}</span></a>'
41438 switch (typeof(template)) {
41442 template = new Roo.Template(template);
41448 var el = template.overwrite(td, {"text": text});
41450 var inner = el.getElementsByTagName("span")[0];
41452 return {"el": el, "inner": inner};
41460 * @class Roo.TabPanelItem
41461 * @extends Roo.util.Observable
41462 * Represents an individual item (tab plus body) in a TabPanel.
41463 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41464 * @param {String} id The id of this TabPanelItem
41465 * @param {String} text The text for the tab of this TabPanelItem
41466 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41468 Roo.bootstrap.panel.TabItem = function(config){
41470 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41471 * @type Roo.TabPanel
41473 this.tabPanel = config.panel;
41475 * The id for this TabPanelItem
41478 this.id = config.id;
41480 this.disabled = false;
41482 this.text = config.text;
41484 this.loaded = false;
41485 this.closable = config.closable;
41488 * The body element for this TabPanelItem.
41489 * @type Roo.Element
41491 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41492 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41493 this.bodyEl.setStyle("display", "block");
41494 this.bodyEl.setStyle("zoom", "1");
41495 //this.hideAction();
41497 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41499 this.el = Roo.get(els.el);
41500 this.inner = Roo.get(els.inner, true);
41501 this.textEl = Roo.bootstrap.version == 4 ?
41502 this.el : Roo.get(this.el.dom.firstChild, true);
41504 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41505 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41508 // this.el.on("mousedown", this.onTabMouseDown, this);
41509 this.el.on("click", this.onTabClick, this);
41511 if(config.closable){
41512 var c = Roo.get(els.close, true);
41513 c.dom.title = this.closeText;
41514 c.addClassOnOver("close-over");
41515 c.on("click", this.closeClick, this);
41521 * Fires when this tab becomes the active tab.
41522 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41523 * @param {Roo.TabPanelItem} this
41527 * @event beforeclose
41528 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41529 * @param {Roo.TabPanelItem} this
41530 * @param {Object} e Set cancel to true on this object to cancel the close.
41532 "beforeclose": true,
41535 * Fires when this tab is closed.
41536 * @param {Roo.TabPanelItem} this
41540 * @event deactivate
41541 * Fires when this tab is no longer the active tab.
41542 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41543 * @param {Roo.TabPanelItem} this
41545 "deactivate" : true
41547 this.hidden = false;
41549 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41552 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41554 purgeListeners : function(){
41555 Roo.util.Observable.prototype.purgeListeners.call(this);
41556 this.el.removeAllListeners();
41559 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41562 this.status_node.addClass("active");
41565 this.tabPanel.stripWrap.repaint();
41567 this.fireEvent("activate", this.tabPanel, this);
41571 * Returns true if this tab is the active tab.
41572 * @return {Boolean}
41574 isActive : function(){
41575 return this.tabPanel.getActiveTab() == this;
41579 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41582 this.status_node.removeClass("active");
41584 this.fireEvent("deactivate", this.tabPanel, this);
41587 hideAction : function(){
41588 this.bodyEl.hide();
41589 this.bodyEl.setStyle("position", "absolute");
41590 this.bodyEl.setLeft("-20000px");
41591 this.bodyEl.setTop("-20000px");
41594 showAction : function(){
41595 this.bodyEl.setStyle("position", "relative");
41596 this.bodyEl.setTop("");
41597 this.bodyEl.setLeft("");
41598 this.bodyEl.show();
41602 * Set the tooltip for the tab.
41603 * @param {String} tooltip The tab's tooltip
41605 setTooltip : function(text){
41606 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41607 this.textEl.dom.qtip = text;
41608 this.textEl.dom.removeAttribute('title');
41610 this.textEl.dom.title = text;
41614 onTabClick : function(e){
41615 e.preventDefault();
41616 this.tabPanel.activate(this.id);
41619 onTabMouseDown : function(e){
41620 e.preventDefault();
41621 this.tabPanel.activate(this.id);
41624 getWidth : function(){
41625 return this.inner.getWidth();
41628 setWidth : function(width){
41629 var iwidth = width - this.linode.getPadding("lr");
41630 this.inner.setWidth(iwidth);
41631 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41632 this.linode.setWidth(width);
41636 * Show or hide the tab
41637 * @param {Boolean} hidden True to hide or false to show.
41639 setHidden : function(hidden){
41640 this.hidden = hidden;
41641 this.linode.setStyle("display", hidden ? "none" : "");
41645 * Returns true if this tab is "hidden"
41646 * @return {Boolean}
41648 isHidden : function(){
41649 return this.hidden;
41653 * Returns the text for this tab
41656 getText : function(){
41660 autoSize : function(){
41661 //this.el.beginMeasure();
41662 this.textEl.setWidth(1);
41664 * #2804 [new] Tabs in Roojs
41665 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41667 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41668 //this.el.endMeasure();
41672 * Sets the text for the tab (Note: this also sets the tooltip text)
41673 * @param {String} text The tab's text and tooltip
41675 setText : function(text){
41677 this.textEl.update(text);
41678 this.setTooltip(text);
41679 //if(!this.tabPanel.resizeTabs){
41680 // this.autoSize();
41684 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41686 activate : function(){
41687 this.tabPanel.activate(this.id);
41691 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41693 disable : function(){
41694 if(this.tabPanel.active != this){
41695 this.disabled = true;
41696 this.status_node.addClass("disabled");
41701 * Enables this TabPanelItem if it was previously disabled.
41703 enable : function(){
41704 this.disabled = false;
41705 this.status_node.removeClass("disabled");
41709 * Sets the content for this TabPanelItem.
41710 * @param {String} content The content
41711 * @param {Boolean} loadScripts true to look for and load scripts
41713 setContent : function(content, loadScripts){
41714 this.bodyEl.update(content, loadScripts);
41718 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41719 * @return {Roo.UpdateManager} The UpdateManager
41721 getUpdateManager : function(){
41722 return this.bodyEl.getUpdateManager();
41726 * Set a URL to be used to load the content for this TabPanelItem.
41727 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41728 * @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)
41729 * @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)
41730 * @return {Roo.UpdateManager} The UpdateManager
41732 setUrl : function(url, params, loadOnce){
41733 if(this.refreshDelegate){
41734 this.un('activate', this.refreshDelegate);
41736 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41737 this.on("activate", this.refreshDelegate);
41738 return this.bodyEl.getUpdateManager();
41742 _handleRefresh : function(url, params, loadOnce){
41743 if(!loadOnce || !this.loaded){
41744 var updater = this.bodyEl.getUpdateManager();
41745 updater.update(url, params, this._setLoaded.createDelegate(this));
41750 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41751 * Will fail silently if the setUrl method has not been called.
41752 * This does not activate the panel, just updates its content.
41754 refresh : function(){
41755 if(this.refreshDelegate){
41756 this.loaded = false;
41757 this.refreshDelegate();
41762 _setLoaded : function(){
41763 this.loaded = true;
41767 closeClick : function(e){
41770 this.fireEvent("beforeclose", this, o);
41771 if(o.cancel !== true){
41772 this.tabPanel.removeTab(this.id);
41776 * The text displayed in the tooltip for the close icon.
41779 closeText : "Close this tab"
41782 * This script refer to:
41783 * Title: International Telephone Input
41784 * Author: Jack O'Connor
41785 * Code version: v12.1.12
41786 * Availability: https://github.com/jackocnr/intl-tel-input.git
41789 Roo.bootstrap.PhoneInputData = function() {
41792 "Afghanistan (افغانستان)",
41797 "Albania (Shqipëri)",
41802 "Algeria (الجزائر)",
41827 "Antigua and Barbuda",
41837 "Armenia (Հայաստան)",
41853 "Austria (Österreich)",
41858 "Azerbaijan (Azərbaycan)",
41868 "Bahrain (البحرين)",
41873 "Bangladesh (বাংলাদেশ)",
41883 "Belarus (Беларусь)",
41888 "Belgium (België)",
41918 "Bosnia and Herzegovina (Босна и Херцеговина)",
41933 "British Indian Ocean Territory",
41938 "British Virgin Islands",
41948 "Bulgaria (България)",
41958 "Burundi (Uburundi)",
41963 "Cambodia (កម្ពុជា)",
41968 "Cameroon (Cameroun)",
41977 ["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"]
41980 "Cape Verde (Kabu Verdi)",
41985 "Caribbean Netherlands",
41996 "Central African Republic (République centrafricaine)",
42016 "Christmas Island",
42022 "Cocos (Keeling) Islands",
42033 "Comoros (جزر القمر)",
42038 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42043 "Congo (Republic) (Congo-Brazzaville)",
42063 "Croatia (Hrvatska)",
42084 "Czech Republic (Česká republika)",
42089 "Denmark (Danmark)",
42104 "Dominican Republic (República Dominicana)",
42108 ["809", "829", "849"]
42126 "Equatorial Guinea (Guinea Ecuatorial)",
42146 "Falkland Islands (Islas Malvinas)",
42151 "Faroe Islands (Føroyar)",
42172 "French Guiana (Guyane française)",
42177 "French Polynesia (Polynésie française)",
42192 "Georgia (საქართველო)",
42197 "Germany (Deutschland)",
42217 "Greenland (Kalaallit Nunaat)",
42254 "Guinea-Bissau (Guiné Bissau)",
42279 "Hungary (Magyarország)",
42284 "Iceland (Ísland)",
42304 "Iraq (العراق)",
42320 "Israel (ישראל)",
42347 "Jordan (الأردن)",
42352 "Kazakhstan (Казахстан)",
42373 "Kuwait (الكويت)",
42378 "Kyrgyzstan (Кыргызстан)",
42388 "Latvia (Latvija)",
42393 "Lebanon (لبنان)",
42408 "Libya (ليبيا)",
42418 "Lithuania (Lietuva)",
42433 "Macedonia (FYROM) (Македонија)",
42438 "Madagascar (Madagasikara)",
42468 "Marshall Islands",
42478 "Mauritania (موريتانيا)",
42483 "Mauritius (Moris)",
42504 "Moldova (Republica Moldova)",
42514 "Mongolia (Монгол)",
42519 "Montenegro (Crna Gora)",
42529 "Morocco (المغرب)",
42535 "Mozambique (Moçambique)",
42540 "Myanmar (Burma) (မြန်မာ)",
42545 "Namibia (Namibië)",
42560 "Netherlands (Nederland)",
42565 "New Caledonia (Nouvelle-Calédonie)",
42600 "North Korea (조선 민주주의 인민 공화국)",
42605 "Northern Mariana Islands",
42621 "Pakistan (پاکستان)",
42631 "Palestine (فلسطين)",
42641 "Papua New Guinea",
42683 "Réunion (La Réunion)",
42689 "Romania (România)",
42705 "Saint Barthélemy",
42716 "Saint Kitts and Nevis",
42726 "Saint Martin (Saint-Martin (partie française))",
42732 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42737 "Saint Vincent and the Grenadines",
42752 "São Tomé and Príncipe (São Tomé e Príncipe)",
42757 "Saudi Arabia (المملكة العربية السعودية)",
42762 "Senegal (Sénégal)",
42792 "Slovakia (Slovensko)",
42797 "Slovenia (Slovenija)",
42807 "Somalia (Soomaaliya)",
42817 "South Korea (대한민국)",
42822 "South Sudan (جنوب السودان)",
42832 "Sri Lanka (ශ්රී ලංකාව)",
42837 "Sudan (السودان)",
42847 "Svalbard and Jan Mayen",
42858 "Sweden (Sverige)",
42863 "Switzerland (Schweiz)",
42868 "Syria (سوريا)",
42913 "Trinidad and Tobago",
42918 "Tunisia (تونس)",
42923 "Turkey (Türkiye)",
42933 "Turks and Caicos Islands",
42943 "U.S. Virgin Islands",
42953 "Ukraine (Україна)",
42958 "United Arab Emirates (الإمارات العربية المتحدة)",
42980 "Uzbekistan (Oʻzbekiston)",
42990 "Vatican City (Città del Vaticano)",
43001 "Vietnam (Việt Nam)",
43006 "Wallis and Futuna (Wallis-et-Futuna)",
43011 "Western Sahara (الصحراء الغربية)",
43017 "Yemen (اليمن)",
43041 * This script refer to:
43042 * Title: International Telephone Input
43043 * Author: Jack O'Connor
43044 * Code version: v12.1.12
43045 * Availability: https://github.com/jackocnr/intl-tel-input.git
43049 * @class Roo.bootstrap.PhoneInput
43050 * @extends Roo.bootstrap.TriggerField
43051 * An input with International dial-code selection
43053 * @cfg {String} defaultDialCode default '+852'
43054 * @cfg {Array} preferedCountries default []
43057 * Create a new PhoneInput.
43058 * @param {Object} config Configuration options
43061 Roo.bootstrap.PhoneInput = function(config) {
43062 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43065 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43067 listWidth: undefined,
43069 selectedClass: 'active',
43071 invalidClass : "has-warning",
43073 validClass: 'has-success',
43075 allowed: '0123456789',
43080 * @cfg {String} defaultDialCode The default dial code when initializing the input
43082 defaultDialCode: '+852',
43085 * @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
43087 preferedCountries: false,
43089 getAutoCreate : function()
43091 var data = Roo.bootstrap.PhoneInputData();
43092 var align = this.labelAlign || this.parentLabelAlign();
43095 this.allCountries = [];
43096 this.dialCodeMapping = [];
43098 for (var i = 0; i < data.length; i++) {
43100 this.allCountries[i] = {
43104 priority: c[3] || 0,
43105 areaCodes: c[4] || null
43107 this.dialCodeMapping[c[2]] = {
43110 priority: c[3] || 0,
43111 areaCodes: c[4] || null
43123 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43124 maxlength: this.max_length,
43125 cls : 'form-control tel-input',
43126 autocomplete: 'new-password'
43129 var hiddenInput = {
43132 cls: 'hidden-tel-input'
43136 hiddenInput.name = this.name;
43139 if (this.disabled) {
43140 input.disabled = true;
43143 var flag_container = {
43160 cls: this.hasFeedback ? 'has-feedback' : '',
43166 cls: 'dial-code-holder',
43173 cls: 'roo-select2-container input-group',
43180 if (this.fieldLabel.length) {
43183 tooltip: 'This field is required'
43189 cls: 'control-label',
43195 html: this.fieldLabel
43198 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43204 if(this.indicatorpos == 'right') {
43205 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43212 if(align == 'left') {
43220 if(this.labelWidth > 12){
43221 label.style = "width: " + this.labelWidth + 'px';
43223 if(this.labelWidth < 13 && this.labelmd == 0){
43224 this.labelmd = this.labelWidth;
43226 if(this.labellg > 0){
43227 label.cls += ' col-lg-' + this.labellg;
43228 input.cls += ' col-lg-' + (12 - this.labellg);
43230 if(this.labelmd > 0){
43231 label.cls += ' col-md-' + this.labelmd;
43232 container.cls += ' col-md-' + (12 - this.labelmd);
43234 if(this.labelsm > 0){
43235 label.cls += ' col-sm-' + this.labelsm;
43236 container.cls += ' col-sm-' + (12 - this.labelsm);
43238 if(this.labelxs > 0){
43239 label.cls += ' col-xs-' + this.labelxs;
43240 container.cls += ' col-xs-' + (12 - this.labelxs);
43250 var settings = this;
43252 ['xs','sm','md','lg'].map(function(size){
43253 if (settings[size]) {
43254 cfg.cls += ' col-' + size + '-' + settings[size];
43258 this.store = new Roo.data.Store({
43259 proxy : new Roo.data.MemoryProxy({}),
43260 reader : new Roo.data.JsonReader({
43271 'name' : 'dialCode',
43275 'name' : 'priority',
43279 'name' : 'areaCodes',
43286 if(!this.preferedCountries) {
43287 this.preferedCountries = [
43294 var p = this.preferedCountries.reverse();
43297 for (var i = 0; i < p.length; i++) {
43298 for (var j = 0; j < this.allCountries.length; j++) {
43299 if(this.allCountries[j].iso2 == p[i]) {
43300 var t = this.allCountries[j];
43301 this.allCountries.splice(j,1);
43302 this.allCountries.unshift(t);
43308 this.store.proxy.data = {
43310 data: this.allCountries
43316 initEvents : function()
43319 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43321 this.indicator = this.indicatorEl();
43322 this.flag = this.flagEl();
43323 this.dialCodeHolder = this.dialCodeHolderEl();
43325 this.trigger = this.el.select('div.flag-box',true).first();
43326 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43331 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43332 _this.list.setWidth(lw);
43335 this.list.on('mouseover', this.onViewOver, this);
43336 this.list.on('mousemove', this.onViewMove, this);
43337 this.inputEl().on("keyup", this.onKeyUp, this);
43338 this.inputEl().on("keypress", this.onKeyPress, this);
43340 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43342 this.view = new Roo.View(this.list, this.tpl, {
43343 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43346 this.view.on('click', this.onViewClick, this);
43347 this.setValue(this.defaultDialCode);
43350 onTriggerClick : function(e)
43352 Roo.log('trigger click');
43357 if(this.isExpanded()){
43359 this.hasFocus = false;
43361 this.store.load({});
43362 this.hasFocus = true;
43367 isExpanded : function()
43369 return this.list.isVisible();
43372 collapse : function()
43374 if(!this.isExpanded()){
43378 Roo.get(document).un('mousedown', this.collapseIf, this);
43379 Roo.get(document).un('mousewheel', this.collapseIf, this);
43380 this.fireEvent('collapse', this);
43384 expand : function()
43388 if(this.isExpanded() || !this.hasFocus){
43392 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43393 this.list.setWidth(lw);
43396 this.restrictHeight();
43398 Roo.get(document).on('mousedown', this.collapseIf, this);
43399 Roo.get(document).on('mousewheel', this.collapseIf, this);
43401 this.fireEvent('expand', this);
43404 restrictHeight : function()
43406 this.list.alignTo(this.inputEl(), this.listAlign);
43407 this.list.alignTo(this.inputEl(), this.listAlign);
43410 onViewOver : function(e, t)
43412 if(this.inKeyMode){
43415 var item = this.view.findItemFromChild(t);
43418 var index = this.view.indexOf(item);
43419 this.select(index, false);
43424 onViewClick : function(view, doFocus, el, e)
43426 var index = this.view.getSelectedIndexes()[0];
43428 var r = this.store.getAt(index);
43431 this.onSelect(r, index);
43433 if(doFocus !== false && !this.blockFocus){
43434 this.inputEl().focus();
43438 onViewMove : function(e, t)
43440 this.inKeyMode = false;
43443 select : function(index, scrollIntoView)
43445 this.selectedIndex = index;
43446 this.view.select(index);
43447 if(scrollIntoView !== false){
43448 var el = this.view.getNode(index);
43450 this.list.scrollChildIntoView(el, false);
43455 createList : function()
43457 this.list = Roo.get(document.body).createChild({
43459 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43460 style: 'display:none'
43463 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43466 collapseIf : function(e)
43468 var in_combo = e.within(this.el);
43469 var in_list = e.within(this.list);
43470 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43472 if (in_combo || in_list || is_list) {
43478 onSelect : function(record, index)
43480 if(this.fireEvent('beforeselect', this, record, index) !== false){
43482 this.setFlagClass(record.data.iso2);
43483 this.setDialCode(record.data.dialCode);
43484 this.hasFocus = false;
43486 this.fireEvent('select', this, record, index);
43490 flagEl : function()
43492 var flag = this.el.select('div.flag',true).first();
43499 dialCodeHolderEl : function()
43501 var d = this.el.select('input.dial-code-holder',true).first();
43508 setDialCode : function(v)
43510 this.dialCodeHolder.dom.value = '+'+v;
43513 setFlagClass : function(n)
43515 this.flag.dom.className = 'flag '+n;
43518 getValue : function()
43520 var v = this.inputEl().getValue();
43521 if(this.dialCodeHolder) {
43522 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43527 setValue : function(v)
43529 var d = this.getDialCode(v);
43531 //invalid dial code
43532 if(v.length == 0 || !d || d.length == 0) {
43534 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43535 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43541 this.setFlagClass(this.dialCodeMapping[d].iso2);
43542 this.setDialCode(d);
43543 this.inputEl().dom.value = v.replace('+'+d,'');
43544 this.hiddenEl().dom.value = this.getValue();
43549 getDialCode : function(v)
43553 if (v.length == 0) {
43554 return this.dialCodeHolder.dom.value;
43558 if (v.charAt(0) != "+") {
43561 var numericChars = "";
43562 for (var i = 1; i < v.length; i++) {
43563 var c = v.charAt(i);
43566 if (this.dialCodeMapping[numericChars]) {
43567 dialCode = v.substr(1, i);
43569 if (numericChars.length == 4) {
43579 this.setValue(this.defaultDialCode);
43583 hiddenEl : function()
43585 return this.el.select('input.hidden-tel-input',true).first();
43588 // after setting val
43589 onKeyUp : function(e){
43590 this.setValue(this.getValue());
43593 onKeyPress : function(e){
43594 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43601 * @class Roo.bootstrap.MoneyField
43602 * @extends Roo.bootstrap.ComboBox
43603 * Bootstrap MoneyField class
43606 * Create a new MoneyField.
43607 * @param {Object} config Configuration options
43610 Roo.bootstrap.MoneyField = function(config) {
43612 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43616 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43619 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43621 allowDecimals : true,
43623 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43625 decimalSeparator : ".",
43627 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43629 decimalPrecision : 0,
43631 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43633 allowNegative : true,
43635 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43639 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43641 minValue : Number.NEGATIVE_INFINITY,
43643 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43645 maxValue : Number.MAX_VALUE,
43647 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43649 minText : "The minimum value for this field is {0}",
43651 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43653 maxText : "The maximum value for this field is {0}",
43655 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43656 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43658 nanText : "{0} is not a valid number",
43660 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43664 * @cfg {String} defaults currency of the MoneyField
43665 * value should be in lkey
43667 defaultCurrency : false,
43669 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43671 thousandsDelimiter : false,
43673 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43684 getAutoCreate : function()
43686 var align = this.labelAlign || this.parentLabelAlign();
43698 cls : 'form-control roo-money-amount-input',
43699 autocomplete: 'new-password'
43702 var hiddenInput = {
43706 cls: 'hidden-number-input'
43709 if(this.max_length) {
43710 input.maxlength = this.max_length;
43714 hiddenInput.name = this.name;
43717 if (this.disabled) {
43718 input.disabled = true;
43721 var clg = 12 - this.inputlg;
43722 var cmd = 12 - this.inputmd;
43723 var csm = 12 - this.inputsm;
43724 var cxs = 12 - this.inputxs;
43728 cls : 'row roo-money-field',
43732 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43736 cls: 'roo-select2-container input-group',
43740 cls : 'form-control roo-money-currency-input',
43741 autocomplete: 'new-password',
43743 name : this.currencyName
43747 cls : 'input-group-addon',
43761 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43765 cls: this.hasFeedback ? 'has-feedback' : '',
43776 if (this.fieldLabel.length) {
43779 tooltip: 'This field is required'
43785 cls: 'control-label',
43791 html: this.fieldLabel
43794 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43800 if(this.indicatorpos == 'right') {
43801 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43808 if(align == 'left') {
43816 if(this.labelWidth > 12){
43817 label.style = "width: " + this.labelWidth + 'px';
43819 if(this.labelWidth < 13 && this.labelmd == 0){
43820 this.labelmd = this.labelWidth;
43822 if(this.labellg > 0){
43823 label.cls += ' col-lg-' + this.labellg;
43824 input.cls += ' col-lg-' + (12 - this.labellg);
43826 if(this.labelmd > 0){
43827 label.cls += ' col-md-' + this.labelmd;
43828 container.cls += ' col-md-' + (12 - this.labelmd);
43830 if(this.labelsm > 0){
43831 label.cls += ' col-sm-' + this.labelsm;
43832 container.cls += ' col-sm-' + (12 - this.labelsm);
43834 if(this.labelxs > 0){
43835 label.cls += ' col-xs-' + this.labelxs;
43836 container.cls += ' col-xs-' + (12 - this.labelxs);
43847 var settings = this;
43849 ['xs','sm','md','lg'].map(function(size){
43850 if (settings[size]) {
43851 cfg.cls += ' col-' + size + '-' + settings[size];
43858 initEvents : function()
43860 this.indicator = this.indicatorEl();
43862 this.initCurrencyEvent();
43864 this.initNumberEvent();
43867 initCurrencyEvent : function()
43870 throw "can not find store for combo";
43873 this.store = Roo.factory(this.store, Roo.data);
43874 this.store.parent = this;
43878 this.triggerEl = this.el.select('.input-group-addon', true).first();
43880 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43885 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43886 _this.list.setWidth(lw);
43889 this.list.on('mouseover', this.onViewOver, this);
43890 this.list.on('mousemove', this.onViewMove, this);
43891 this.list.on('scroll', this.onViewScroll, this);
43894 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43897 this.view = new Roo.View(this.list, this.tpl, {
43898 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43901 this.view.on('click', this.onViewClick, this);
43903 this.store.on('beforeload', this.onBeforeLoad, this);
43904 this.store.on('load', this.onLoad, this);
43905 this.store.on('loadexception', this.onLoadException, this);
43907 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43908 "up" : function(e){
43909 this.inKeyMode = true;
43913 "down" : function(e){
43914 if(!this.isExpanded()){
43915 this.onTriggerClick();
43917 this.inKeyMode = true;
43922 "enter" : function(e){
43925 if(this.fireEvent("specialkey", this, e)){
43926 this.onViewClick(false);
43932 "esc" : function(e){
43936 "tab" : function(e){
43939 if(this.fireEvent("specialkey", this, e)){
43940 this.onViewClick(false);
43948 doRelay : function(foo, bar, hname){
43949 if(hname == 'down' || this.scope.isExpanded()){
43950 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43958 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43962 initNumberEvent : function(e)
43964 this.inputEl().on("keydown" , this.fireKey, this);
43965 this.inputEl().on("focus", this.onFocus, this);
43966 this.inputEl().on("blur", this.onBlur, this);
43968 this.inputEl().relayEvent('keyup', this);
43970 if(this.indicator){
43971 this.indicator.addClass('invisible');
43974 this.originalValue = this.getValue();
43976 if(this.validationEvent == 'keyup'){
43977 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43978 this.inputEl().on('keyup', this.filterValidation, this);
43980 else if(this.validationEvent !== false){
43981 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43984 if(this.selectOnFocus){
43985 this.on("focus", this.preFocus, this);
43988 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43989 this.inputEl().on("keypress", this.filterKeys, this);
43991 this.inputEl().relayEvent('keypress', this);
43994 var allowed = "0123456789";
43996 if(this.allowDecimals){
43997 allowed += this.decimalSeparator;
44000 if(this.allowNegative){
44004 if(this.thousandsDelimiter) {
44008 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44010 var keyPress = function(e){
44012 var k = e.getKey();
44014 var c = e.getCharCode();
44017 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44018 allowed.indexOf(String.fromCharCode(c)) === -1
44024 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44028 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44033 this.inputEl().on("keypress", keyPress, this);
44037 onTriggerClick : function(e)
44044 this.loadNext = false;
44046 if(this.isExpanded()){
44051 this.hasFocus = true;
44053 if(this.triggerAction == 'all') {
44054 this.doQuery(this.allQuery, true);
44058 this.doQuery(this.getRawValue());
44061 getCurrency : function()
44063 var v = this.currencyEl().getValue();
44068 restrictHeight : function()
44070 this.list.alignTo(this.currencyEl(), this.listAlign);
44071 this.list.alignTo(this.currencyEl(), this.listAlign);
44074 onViewClick : function(view, doFocus, el, e)
44076 var index = this.view.getSelectedIndexes()[0];
44078 var r = this.store.getAt(index);
44081 this.onSelect(r, index);
44085 onSelect : function(record, index){
44087 if(this.fireEvent('beforeselect', this, record, index) !== false){
44089 this.setFromCurrencyData(index > -1 ? record.data : false);
44093 this.fireEvent('select', this, record, index);
44097 setFromCurrencyData : function(o)
44101 this.lastCurrency = o;
44103 if (this.currencyField) {
44104 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44106 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44109 this.lastSelectionText = currency;
44111 //setting default currency
44112 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44113 this.setCurrency(this.defaultCurrency);
44117 this.setCurrency(currency);
44120 setFromData : function(o)
44124 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44126 this.setFromCurrencyData(c);
44131 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44133 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44136 this.setValue(value);
44140 setCurrency : function(v)
44142 this.currencyValue = v;
44145 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44150 setValue : function(v)
44152 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44158 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44160 this.inputEl().dom.value = (v == '') ? '' :
44161 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44163 if(!this.allowZero && v === '0') {
44164 this.hiddenEl().dom.value = '';
44165 this.inputEl().dom.value = '';
44172 getRawValue : function()
44174 var v = this.inputEl().getValue();
44179 getValue : function()
44181 return this.fixPrecision(this.parseValue(this.getRawValue()));
44184 parseValue : function(value)
44186 if(this.thousandsDelimiter) {
44188 r = new RegExp(",", "g");
44189 value = value.replace(r, "");
44192 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44193 return isNaN(value) ? '' : value;
44197 fixPrecision : function(value)
44199 if(this.thousandsDelimiter) {
44201 r = new RegExp(",", "g");
44202 value = value.replace(r, "");
44205 var nan = isNaN(value);
44207 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44208 return nan ? '' : value;
44210 return parseFloat(value).toFixed(this.decimalPrecision);
44213 decimalPrecisionFcn : function(v)
44215 return Math.floor(v);
44218 validateValue : function(value)
44220 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44224 var num = this.parseValue(value);
44227 this.markInvalid(String.format(this.nanText, value));
44231 if(num < this.minValue){
44232 this.markInvalid(String.format(this.minText, this.minValue));
44236 if(num > this.maxValue){
44237 this.markInvalid(String.format(this.maxText, this.maxValue));
44244 validate : function()
44246 if(this.disabled || this.allowBlank){
44251 var currency = this.getCurrency();
44253 if(this.validateValue(this.getRawValue()) && currency.length){
44258 this.markInvalid();
44262 getName: function()
44267 beforeBlur : function()
44273 var v = this.parseValue(this.getRawValue());
44280 onBlur : function()
44284 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44285 //this.el.removeClass(this.focusClass);
44288 this.hasFocus = false;
44290 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44294 var v = this.getValue();
44296 if(String(v) !== String(this.startValue)){
44297 this.fireEvent('change', this, v, this.startValue);
44300 this.fireEvent("blur", this);
44303 inputEl : function()
44305 return this.el.select('.roo-money-amount-input', true).first();
44308 currencyEl : function()
44310 return this.el.select('.roo-money-currency-input', true).first();
44313 hiddenEl : function()
44315 return this.el.select('input.hidden-number-input',true).first();
44319 * @class Roo.bootstrap.BezierSignature
44320 * @extends Roo.bootstrap.Component
44321 * Bootstrap BezierSignature class
44322 * This script refer to:
44323 * Title: Signature Pad
44325 * Availability: https://github.com/szimek/signature_pad
44328 * Create a new BezierSignature
44329 * @param {Object} config The config object
44332 Roo.bootstrap.BezierSignature = function(config){
44333 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44339 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44346 mouse_btn_down: true,
44349 * @cfg {int} canvas height
44351 canvas_height: '200px',
44354 * @cfg {float|function} Radius of a single dot.
44359 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44364 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44369 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44374 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44379 * @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.
44381 bg_color: 'rgba(0, 0, 0, 0)',
44384 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44386 dot_color: 'black',
44389 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44391 velocity_filter_weight: 0.7,
44394 * @cfg {function} Callback when stroke begin.
44399 * @cfg {function} Callback when stroke end.
44403 getAutoCreate : function()
44405 var cls = 'roo-signature column';
44408 cls += ' ' + this.cls;
44418 for(var i = 0; i < col_sizes.length; i++) {
44419 if(this[col_sizes[i]]) {
44420 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44430 cls: 'roo-signature-body',
44434 cls: 'roo-signature-body-canvas',
44435 height: this.canvas_height,
44436 width: this.canvas_width
44443 style: 'display: none'
44451 initEvents: function()
44453 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44455 var canvas = this.canvasEl();
44457 // mouse && touch event swapping...
44458 canvas.dom.style.touchAction = 'none';
44459 canvas.dom.style.msTouchAction = 'none';
44461 this.mouse_btn_down = false;
44462 canvas.on('mousedown', this._handleMouseDown, this);
44463 canvas.on('mousemove', this._handleMouseMove, this);
44464 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44466 if (window.PointerEvent) {
44467 canvas.on('pointerdown', this._handleMouseDown, this);
44468 canvas.on('pointermove', this._handleMouseMove, this);
44469 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44472 if ('ontouchstart' in window) {
44473 canvas.on('touchstart', this._handleTouchStart, this);
44474 canvas.on('touchmove', this._handleTouchMove, this);
44475 canvas.on('touchend', this._handleTouchEnd, this);
44478 Roo.EventManager.onWindowResize(this.resize, this, true);
44480 // file input event
44481 this.fileEl().on('change', this.uploadImage, this);
44488 resize: function(){
44490 var canvas = this.canvasEl().dom;
44491 var ctx = this.canvasElCtx();
44492 var img_data = false;
44494 if(canvas.width > 0) {
44495 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44497 // setting canvas width will clean img data
44500 var style = window.getComputedStyle ?
44501 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44503 var padding_left = parseInt(style.paddingLeft) || 0;
44504 var padding_right = parseInt(style.paddingRight) || 0;
44506 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44509 ctx.putImageData(img_data, 0, 0);
44513 _handleMouseDown: function(e)
44515 if (e.browserEvent.which === 1) {
44516 this.mouse_btn_down = true;
44517 this.strokeBegin(e);
44521 _handleMouseMove: function (e)
44523 if (this.mouse_btn_down) {
44524 this.strokeMoveUpdate(e);
44528 _handleMouseUp: function (e)
44530 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44531 this.mouse_btn_down = false;
44536 _handleTouchStart: function (e) {
44538 e.preventDefault();
44539 if (e.browserEvent.targetTouches.length === 1) {
44540 // var touch = e.browserEvent.changedTouches[0];
44541 // this.strokeBegin(touch);
44543 this.strokeBegin(e); // assume e catching the correct xy...
44547 _handleTouchMove: function (e) {
44548 e.preventDefault();
44549 // var touch = event.targetTouches[0];
44550 // _this._strokeMoveUpdate(touch);
44551 this.strokeMoveUpdate(e);
44554 _handleTouchEnd: function (e) {
44555 var wasCanvasTouched = e.target === this.canvasEl().dom;
44556 if (wasCanvasTouched) {
44557 e.preventDefault();
44558 // var touch = event.changedTouches[0];
44559 // _this._strokeEnd(touch);
44564 reset: function () {
44565 this._lastPoints = [];
44566 this._lastVelocity = 0;
44567 this._lastWidth = (this.min_width + this.max_width) / 2;
44568 this.canvasElCtx().fillStyle = this.dot_color;
44571 strokeMoveUpdate: function(e)
44573 this.strokeUpdate(e);
44575 if (this.throttle) {
44576 this.throttleStroke(this.strokeUpdate, this.throttle);
44579 this.strokeUpdate(e);
44583 strokeBegin: function(e)
44585 var newPointGroup = {
44586 color: this.dot_color,
44590 if (typeof this.onBegin === 'function') {
44594 this.curve_data.push(newPointGroup);
44596 this.strokeUpdate(e);
44599 strokeUpdate: function(e)
44601 var rect = this.canvasEl().dom.getBoundingClientRect();
44602 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44603 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44604 var lastPoints = lastPointGroup.points;
44605 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44606 var isLastPointTooClose = lastPoint
44607 ? point.distanceTo(lastPoint) <= this.min_distance
44609 var color = lastPointGroup.color;
44610 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44611 var curve = this.addPoint(point);
44613 this.drawDot({color: color, point: point});
44616 this.drawCurve({color: color, curve: curve});
44626 strokeEnd: function(e)
44628 this.strokeUpdate(e);
44629 if (typeof this.onEnd === 'function') {
44634 addPoint: function (point) {
44635 var _lastPoints = this._lastPoints;
44636 _lastPoints.push(point);
44637 if (_lastPoints.length > 2) {
44638 if (_lastPoints.length === 3) {
44639 _lastPoints.unshift(_lastPoints[0]);
44641 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44642 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44643 _lastPoints.shift();
44649 calculateCurveWidths: function (startPoint, endPoint) {
44650 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44651 (1 - this.velocity_filter_weight) * this._lastVelocity;
44653 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44656 start: this._lastWidth
44659 this._lastVelocity = velocity;
44660 this._lastWidth = newWidth;
44664 drawDot: function (_a) {
44665 var color = _a.color, point = _a.point;
44666 var ctx = this.canvasElCtx();
44667 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44669 this.drawCurveSegment(point.x, point.y, width);
44671 ctx.fillStyle = color;
44675 drawCurve: function (_a) {
44676 var color = _a.color, curve = _a.curve;
44677 var ctx = this.canvasElCtx();
44678 var widthDelta = curve.endWidth - curve.startWidth;
44679 var drawSteps = Math.floor(curve.length()) * 2;
44681 ctx.fillStyle = color;
44682 for (var i = 0; i < drawSteps; i += 1) {
44683 var t = i / drawSteps;
44689 var x = uuu * curve.startPoint.x;
44690 x += 3 * uu * t * curve.control1.x;
44691 x += 3 * u * tt * curve.control2.x;
44692 x += ttt * curve.endPoint.x;
44693 var y = uuu * curve.startPoint.y;
44694 y += 3 * uu * t * curve.control1.y;
44695 y += 3 * u * tt * curve.control2.y;
44696 y += ttt * curve.endPoint.y;
44697 var width = curve.startWidth + ttt * widthDelta;
44698 this.drawCurveSegment(x, y, width);
44704 drawCurveSegment: function (x, y, width) {
44705 var ctx = this.canvasElCtx();
44707 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44708 this.is_empty = false;
44713 var ctx = this.canvasElCtx();
44714 var canvas = this.canvasEl().dom;
44715 ctx.fillStyle = this.bg_color;
44716 ctx.clearRect(0, 0, canvas.width, canvas.height);
44717 ctx.fillRect(0, 0, canvas.width, canvas.height);
44718 this.curve_data = [];
44720 this.is_empty = true;
44725 return this.el.select('input',true).first();
44728 canvasEl: function()
44730 return this.el.select('canvas',true).first();
44733 canvasElCtx: function()
44735 return this.el.select('canvas',true).first().dom.getContext('2d');
44738 getImage: function(type)
44740 if(this.is_empty) {
44745 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44748 drawFromImage: function(img_src)
44750 var img = new Image();
44752 img.onload = function(){
44753 this.canvasElCtx().drawImage(img, 0, 0);
44758 this.is_empty = false;
44761 selectImage: function()
44763 this.fileEl().dom.click();
44766 uploadImage: function(e)
44768 var reader = new FileReader();
44770 reader.onload = function(e){
44771 var img = new Image();
44772 img.onload = function(){
44774 this.canvasElCtx().drawImage(img, 0, 0);
44776 img.src = e.target.result;
44779 reader.readAsDataURL(e.target.files[0]);
44782 // Bezier Point Constructor
44783 Point: (function () {
44784 function Point(x, y, time) {
44787 this.time = time || Date.now();
44789 Point.prototype.distanceTo = function (start) {
44790 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44792 Point.prototype.equals = function (other) {
44793 return this.x === other.x && this.y === other.y && this.time === other.time;
44795 Point.prototype.velocityFrom = function (start) {
44796 return this.time !== start.time
44797 ? this.distanceTo(start) / (this.time - start.time)
44804 // Bezier Constructor
44805 Bezier: (function () {
44806 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44807 this.startPoint = startPoint;
44808 this.control2 = control2;
44809 this.control1 = control1;
44810 this.endPoint = endPoint;
44811 this.startWidth = startWidth;
44812 this.endWidth = endWidth;
44814 Bezier.fromPoints = function (points, widths, scope) {
44815 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44816 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44817 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44819 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44820 var dx1 = s1.x - s2.x;
44821 var dy1 = s1.y - s2.y;
44822 var dx2 = s2.x - s3.x;
44823 var dy2 = s2.y - s3.y;
44824 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44825 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44826 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44827 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44828 var dxm = m1.x - m2.x;
44829 var dym = m1.y - m2.y;
44830 var k = l2 / (l1 + l2);
44831 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44832 var tx = s2.x - cm.x;
44833 var ty = s2.y - cm.y;
44835 c1: new scope.Point(m1.x + tx, m1.y + ty),
44836 c2: new scope.Point(m2.x + tx, m2.y + ty)
44839 Bezier.prototype.length = function () {
44844 for (var i = 0; i <= steps; i += 1) {
44846 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44847 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44849 var xdiff = cx - px;
44850 var ydiff = cy - py;
44851 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44858 Bezier.prototype.point = function (t, start, c1, c2, end) {
44859 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44860 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44861 + (3.0 * c2 * (1.0 - t) * t * t)
44862 + (end * t * t * t);
44867 throttleStroke: function(fn, wait) {
44868 if (wait === void 0) { wait = 250; }
44870 var timeout = null;
44874 var later = function () {
44875 previous = Date.now();
44877 result = fn.apply(storedContext, storedArgs);
44879 storedContext = null;
44883 return function wrapper() {
44885 for (var _i = 0; _i < arguments.length; _i++) {
44886 args[_i] = arguments[_i];
44888 var now = Date.now();
44889 var remaining = wait - (now - previous);
44890 storedContext = this;
44892 if (remaining <= 0 || remaining > wait) {
44894 clearTimeout(timeout);
44898 result = fn.apply(storedContext, storedArgs);
44900 storedContext = null;
44904 else if (!timeout) {
44905 timeout = window.setTimeout(later, remaining);