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 = (typeof(cfg.style) == 'undefined' ? this.style : 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
3046 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3049 * Create a new Input
3050 * @param {Object} config The config object
3053 Roo.bootstrap.Img = function(config){
3054 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3060 * The img click event for the img.
3061 * @param {Roo.EventObject} e
3066 * The when any image loads
3067 * @param {Roo.EventObject} e
3073 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3075 imgResponsive: true,
3084 backgroundContain : false,
3086 getAutoCreate : function()
3088 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3089 return this.createSingleImg();
3094 cls: 'roo-image-responsive-group',
3099 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3101 if(!_this[size + 'Url']){
3107 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3108 html: _this.html || cfg.html,
3109 src: _this[size + 'Url']
3112 img.cls += ' roo-image-responsive-' + size;
3114 var s = ['xs', 'sm', 'md', 'lg'];
3116 s.splice(s.indexOf(size), 1);
3118 Roo.each(s, function(ss){
3119 img.cls += ' hidden-' + ss;
3122 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3123 cfg.cls += ' img-' + _this.border;
3127 cfg.alt = _this.alt;
3140 a.target = _this.target;
3144 cfg.cn.push((_this.href) ? a : img);
3151 createSingleImg : function()
3155 cls: (this.imgResponsive) ? 'img-responsive' : '',
3157 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3160 if (this.backgroundContain) {
3161 cfg.cls += ' background-contain';
3164 cfg.html = this.html || cfg.html;
3166 if (this.backgroundContain) {
3167 cfg.style="background-image: url(" + this.src + ')';
3169 cfg.src = this.src || cfg.src;
3172 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3173 cfg.cls += ' img-' + this.border;
3190 a.target = this.target;
3195 return (this.href) ? a : cfg;
3198 initEvents: function()
3201 this.el.on('click', this.onClick, this);
3203 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3204 this.el.on('load', this.onImageLoad, this);
3206 // not sure if this works.. not tested
3207 this.el.select('img', true).on('load', this.onImageLoad, this);
3212 onClick : function(e)
3214 Roo.log('img onclick');
3215 this.fireEvent('click', this, e);
3217 onImageLoad: function(e)
3219 Roo.log('img load');
3220 this.fireEvent('load', this, e);
3224 * Sets the url of the image - used to update it
3225 * @param {String} url the url of the image
3228 setSrc : function(url)
3232 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3233 if (this.backgroundContain) {
3234 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3236 this.el.dom.src = url;
3241 this.el.select('img', true).first().dom.src = url;
3257 * @class Roo.bootstrap.Link
3258 * @extends Roo.bootstrap.Component
3259 * Bootstrap Link Class
3260 * @cfg {String} alt image alternative text
3261 * @cfg {String} href a tag href
3262 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3263 * @cfg {String} html the content of the link.
3264 * @cfg {String} anchor name for the anchor link
3265 * @cfg {String} fa - favicon
3267 * @cfg {Boolean} preventDefault (true | false) default false
3271 * Create a new Input
3272 * @param {Object} config The config object
3275 Roo.bootstrap.Link = function(config){
3276 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3282 * The img click event for the img.
3283 * @param {Roo.EventObject} e
3289 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3293 preventDefault: false,
3299 getAutoCreate : function()
3301 var html = this.html || '';
3303 if (this.fa !== false) {
3304 html = '<i class="fa fa-' + this.fa + '"></i>';
3309 // anchor's do not require html/href...
3310 if (this.anchor === false) {
3312 cfg.href = this.href || '#';
3314 cfg.name = this.anchor;
3315 if (this.html !== false || this.fa !== false) {
3318 if (this.href !== false) {
3319 cfg.href = this.href;
3323 if(this.alt !== false){
3328 if(this.target !== false) {
3329 cfg.target = this.target;
3335 initEvents: function() {
3337 if(!this.href || this.preventDefault){
3338 this.el.on('click', this.onClick, this);
3342 onClick : function(e)
3344 if(this.preventDefault){
3347 //Roo.log('img onclick');
3348 this.fireEvent('click', this, e);
3361 * @class Roo.bootstrap.Header
3362 * @extends Roo.bootstrap.Component
3363 * Bootstrap Header class
3364 * @cfg {String} html content of header
3365 * @cfg {Number} level (1|2|3|4|5|6) default 1
3368 * Create a new Header
3369 * @param {Object} config The config object
3373 Roo.bootstrap.Header = function(config){
3374 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3377 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3385 getAutoCreate : function(){
3390 tag: 'h' + (1 *this.level),
3391 html: this.html || ''
3403 * Ext JS Library 1.1.1
3404 * Copyright(c) 2006-2007, Ext JS, LLC.
3406 * Originally Released Under LGPL - original licence link has changed is not relivant.
3409 * <script type="text/javascript">
3413 * @class Roo.bootstrap.MenuMgr
3414 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3417 Roo.bootstrap.MenuMgr = function(){
3418 var menus, active, groups = {}, attached = false, lastShow = new Date();
3420 // private - called when first menu is created
3423 active = new Roo.util.MixedCollection();
3424 Roo.get(document).addKeyListener(27, function(){
3425 if(active.length > 0){
3433 if(active && active.length > 0){
3434 var c = active.clone();
3444 if(active.length < 1){
3445 Roo.get(document).un("mouseup", onMouseDown);
3453 var last = active.last();
3454 lastShow = new Date();
3457 Roo.get(document).on("mouseup", onMouseDown);
3462 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3463 m.parentMenu.activeChild = m;
3464 }else if(last && last.isVisible()){
3465 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3470 function onBeforeHide(m){
3472 m.activeChild.hide();
3474 if(m.autoHideTimer){
3475 clearTimeout(m.autoHideTimer);
3476 delete m.autoHideTimer;
3481 function onBeforeShow(m){
3482 var pm = m.parentMenu;
3483 if(!pm && !m.allowOtherMenus){
3485 }else if(pm && pm.activeChild && active != m){
3486 pm.activeChild.hide();
3490 // private this should really trigger on mouseup..
3491 function onMouseDown(e){
3492 Roo.log("on Mouse Up");
3494 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3495 Roo.log("MenuManager hideAll");
3504 function onBeforeCheck(mi, state){
3506 var g = groups[mi.group];
3507 for(var i = 0, l = g.length; i < l; i++){
3509 g[i].setChecked(false);
3518 * Hides all menus that are currently visible
3520 hideAll : function(){
3525 register : function(menu){
3529 menus[menu.id] = menu;
3530 menu.on("beforehide", onBeforeHide);
3531 menu.on("hide", onHide);
3532 menu.on("beforeshow", onBeforeShow);
3533 menu.on("show", onShow);
3535 if(g && menu.events["checkchange"]){
3539 groups[g].push(menu);
3540 menu.on("checkchange", onCheck);
3545 * Returns a {@link Roo.menu.Menu} object
3546 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3547 * be used to generate and return a new Menu instance.
3549 get : function(menu){
3550 if(typeof menu == "string"){ // menu id
3552 }else if(menu.events){ // menu instance
3555 /*else if(typeof menu.length == 'number'){ // array of menu items?
3556 return new Roo.bootstrap.Menu({items:menu});
3557 }else{ // otherwise, must be a config
3558 return new Roo.bootstrap.Menu(menu);
3565 unregister : function(menu){
3566 delete menus[menu.id];
3567 menu.un("beforehide", onBeforeHide);
3568 menu.un("hide", onHide);
3569 menu.un("beforeshow", onBeforeShow);
3570 menu.un("show", onShow);
3572 if(g && menu.events["checkchange"]){
3573 groups[g].remove(menu);
3574 menu.un("checkchange", onCheck);
3579 registerCheckable : function(menuItem){
3580 var g = menuItem.group;
3585 groups[g].push(menuItem);
3586 menuItem.on("beforecheckchange", onBeforeCheck);
3591 unregisterCheckable : function(menuItem){
3592 var g = menuItem.group;
3594 groups[g].remove(menuItem);
3595 menuItem.un("beforecheckchange", onBeforeCheck);
3607 * @class Roo.bootstrap.Menu
3608 * @extends Roo.bootstrap.Component
3609 * Bootstrap Menu class - container for MenuItems
3610 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3611 * @cfg {bool} hidden if the menu should be hidden when rendered.
3612 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3613 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3614 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3615 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3619 * @param {Object} config The config object
3623 Roo.bootstrap.Menu = function(config){
3625 if (config.type == 'treeview') {
3626 // normally menu's are drawn attached to the document to handle layering etc..
3627 // however treeview (used by the docs menu is drawn into the parent element)
3628 this.container_method = 'getChildContainer';
3631 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3632 if (this.registerMenu && this.type != 'treeview') {
3633 Roo.bootstrap.MenuMgr.register(this);
3640 * Fires before this menu is displayed (return false to block)
3641 * @param {Roo.menu.Menu} this
3646 * Fires before this menu is hidden (return false to block)
3647 * @param {Roo.menu.Menu} this
3652 * Fires after this menu is displayed
3653 * @param {Roo.menu.Menu} this
3658 * Fires after this menu is hidden
3659 * @param {Roo.menu.Menu} this
3664 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3665 * @param {Roo.menu.Menu} this
3666 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3667 * @param {Roo.EventObject} e
3672 * Fires when the mouse is hovering over this menu
3673 * @param {Roo.menu.Menu} this
3674 * @param {Roo.EventObject} e
3675 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680 * Fires when the mouse exits this menu
3681 * @param {Roo.menu.Menu} this
3682 * @param {Roo.EventObject} e
3683 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3688 * Fires when a menu item contained in this menu is clicked
3689 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3690 * @param {Roo.EventObject} e
3694 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3697 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3701 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3704 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3706 registerMenu : true,
3708 menuItems :false, // stores the menu items..
3718 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3720 hideTrigger : false,
3725 getChildContainer : function() {
3729 getAutoCreate : function(){
3731 //if (['right'].indexOf(this.align)!==-1) {
3732 // cfg.cn[1].cls += ' pull-right'
3737 cls : 'dropdown-menu shadow' ,
3738 style : 'z-index:1000'
3742 if (this.type === 'submenu') {
3743 cfg.cls = 'submenu active';
3745 if (this.type === 'treeview') {
3746 cfg.cls = 'treeview-menu';
3751 initEvents : function() {
3753 // Roo.log("ADD event");
3754 // Roo.log(this.triggerEl.dom);
3755 if (this.triggerEl) {
3757 this.triggerEl.on('click', this.onTriggerClick, this);
3759 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3761 if (!this.hideTrigger) {
3762 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3763 // dropdown toggle on the 'a' in BS4?
3764 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3766 this.triggerEl.addClass('dropdown-toggle');
3772 this.el.on('touchstart' , this.onTouch, this);
3774 this.el.on('click' , this.onClick, this);
3776 this.el.on("mouseover", this.onMouseOver, this);
3777 this.el.on("mouseout", this.onMouseOut, this);
3781 findTargetItem : function(e)
3783 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3787 //Roo.log(t); Roo.log(t.id);
3789 //Roo.log(this.menuitems);
3790 return this.menuitems.get(t.id);
3792 //return this.items.get(t.menuItemId);
3798 onTouch : function(e)
3800 Roo.log("menu.onTouch");
3801 //e.stopEvent(); this make the user popdown broken
3805 onClick : function(e)
3807 Roo.log("menu.onClick");
3809 var t = this.findTargetItem(e);
3810 if(!t || t.isContainer){
3815 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3816 if(t == this.activeItem && t.shouldDeactivate(e)){
3817 this.activeItem.deactivate();
3818 delete this.activeItem;
3822 this.setActiveItem(t, true);
3830 Roo.log('pass click event');
3834 this.fireEvent("click", this, t, e);
3838 if(!t.href.length || t.href == '#'){
3839 (function() { _this.hide(); }).defer(100);
3844 onMouseOver : function(e){
3845 var t = this.findTargetItem(e);
3848 // if(t.canActivate && !t.disabled){
3849 // this.setActiveItem(t, true);
3853 this.fireEvent("mouseover", this, e, t);
3855 isVisible : function(){
3856 return !this.hidden;
3858 onMouseOut : function(e){
3859 var t = this.findTargetItem(e);
3862 // if(t == this.activeItem && t.shouldDeactivate(e)){
3863 // this.activeItem.deactivate();
3864 // delete this.activeItem;
3867 this.fireEvent("mouseout", this, e, t);
3872 * Displays this menu relative to another element
3873 * @param {String/HTMLElement/Roo.Element} element The element to align to
3874 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3875 * the element (defaults to this.defaultAlign)
3876 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3878 show : function(el, pos, parentMenu)
3880 if (false === this.fireEvent("beforeshow", this)) {
3881 Roo.log("show canceled");
3884 this.parentMenu = parentMenu;
3888 this.el.addClass('show'); // show otherwise we do not know how big we are..
3890 var xy = this.el.getAlignToXY(el, pos);
3892 // bl-tl << left align below
3893 // tl-bl << left align
3895 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3896 // if it goes to far to the right.. -> align left.
3897 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3900 // was left align - go right?
3901 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3904 // goes down the bottom
3905 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3907 var a = this.align.replace('?', '').split('-');
3908 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3912 this.showAt( xy , parentMenu, false);
3915 * Displays this menu at a specific xy position
3916 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3917 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3919 showAt : function(xy, parentMenu, /* private: */_e){
3920 this.parentMenu = parentMenu;
3925 this.fireEvent("beforeshow", this);
3926 //xy = this.el.adjustForConstraints(xy);
3930 this.hideMenuItems();
3931 this.hidden = false;
3932 if (this.triggerEl) {
3933 this.triggerEl.addClass('open');
3936 this.el.addClass('show');
3940 // reassign x when hitting right
3942 // reassign y when hitting bottom
3944 // but the list may align on trigger left or trigger top... should it be a properity?
3946 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3951 this.fireEvent("show", this);
3957 this.doFocus.defer(50, this);
3961 doFocus : function(){
3963 this.focusEl.focus();
3968 * Hides this menu and optionally all parent menus
3969 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3971 hide : function(deep)
3973 if (false === this.fireEvent("beforehide", this)) {
3974 Roo.log("hide canceled");
3977 this.hideMenuItems();
3978 if(this.el && this.isVisible()){
3980 if(this.activeItem){
3981 this.activeItem.deactivate();
3982 this.activeItem = null;
3984 if (this.triggerEl) {
3985 this.triggerEl.removeClass('open');
3988 this.el.removeClass('show');
3990 this.fireEvent("hide", this);
3992 if(deep === true && this.parentMenu){
3993 this.parentMenu.hide(true);
3997 onTriggerClick : function(e)
3999 Roo.log('trigger click');
4001 var target = e.getTarget();
4003 Roo.log(target.nodeName.toLowerCase());
4005 if(target.nodeName.toLowerCase() === 'i'){
4011 onTriggerPress : function(e)
4013 Roo.log('trigger press');
4014 //Roo.log(e.getTarget());
4015 // Roo.log(this.triggerEl.dom);
4017 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4018 var pel = Roo.get(e.getTarget());
4019 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4020 Roo.log('is treeview or dropdown?');
4024 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4028 if (this.isVisible()) {
4034 this.show(this.triggerEl, this.align, false);
4037 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4044 hideMenuItems : function()
4046 Roo.log("hide Menu Items");
4051 this.el.select('.open',true).each(function(aa) {
4053 aa.removeClass('open');
4057 addxtypeChild : function (tree, cntr) {
4058 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4060 this.menuitems.add(comp);
4072 this.getEl().dom.innerHTML = '';
4073 this.menuitems.clear();
4087 * @class Roo.bootstrap.MenuItem
4088 * @extends Roo.bootstrap.Component
4089 * Bootstrap MenuItem class
4090 * @cfg {String} html the menu label
4091 * @cfg {String} href the link
4092 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4093 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4094 * @cfg {Boolean} active used on sidebars to highlight active itesm
4095 * @cfg {String} fa favicon to show on left of menu item.
4096 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4100 * Create a new MenuItem
4101 * @param {Object} config The config object
4105 Roo.bootstrap.MenuItem = function(config){
4106 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4111 * The raw click event for the entire grid.
4112 * @param {Roo.bootstrap.MenuItem} this
4113 * @param {Roo.EventObject} e
4119 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4123 preventDefault: false,
4124 isContainer : false,
4128 getAutoCreate : function(){
4130 if(this.isContainer){
4133 cls: 'dropdown-menu-item '
4143 cls : 'dropdown-item',
4148 if (this.fa !== false) {
4151 cls : 'fa fa-' + this.fa
4160 cls: 'dropdown-menu-item',
4163 if (this.parent().type == 'treeview') {
4164 cfg.cls = 'treeview-menu';
4167 cfg.cls += ' active';
4172 anc.href = this.href || cfg.cn[0].href ;
4173 ctag.html = this.html || cfg.cn[0].html ;
4177 initEvents: function()
4179 if (this.parent().type == 'treeview') {
4180 this.el.select('a').on('click', this.onClick, this);
4184 this.menu.parentType = this.xtype;
4185 this.menu.triggerEl = this.el;
4186 this.menu = this.addxtype(Roo.apply({}, this.menu));
4190 onClick : function(e)
4192 Roo.log('item on click ');
4194 if(this.preventDefault){
4197 //this.parent().hideMenuItems();
4199 this.fireEvent('click', this, e);
4218 * @class Roo.bootstrap.MenuSeparator
4219 * @extends Roo.bootstrap.Component
4220 * Bootstrap MenuSeparator class
4223 * Create a new MenuItem
4224 * @param {Object} config The config object
4228 Roo.bootstrap.MenuSeparator = function(config){
4229 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4232 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4234 getAutoCreate : function(){
4253 * @class Roo.bootstrap.Modal
4254 * @extends Roo.bootstrap.Component
4255 * Bootstrap Modal class
4256 * @cfg {String} title Title of dialog
4257 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4258 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4259 * @cfg {Boolean} specificTitle default false
4260 * @cfg {Array} buttons Array of buttons or standard button set..
4261 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4262 * @cfg {Boolean} animate default true
4263 * @cfg {Boolean} allow_close default true
4264 * @cfg {Boolean} fitwindow default false
4265 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4266 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4267 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4268 * @cfg {String} size (sm|lg|xl) default empty
4269 * @cfg {Number} max_width set the max width of modal
4270 * @cfg {Boolean} editableTitle can the title be edited
4275 * Create a new Modal Dialog
4276 * @param {Object} config The config object
4279 Roo.bootstrap.Modal = function(config){
4280 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4285 * The raw btnclick event for the button
4286 * @param {Roo.EventObject} e
4291 * Fire when dialog resize
4292 * @param {Roo.bootstrap.Modal} this
4293 * @param {Roo.EventObject} e
4297 * @event titlechanged
4298 * Fire when the editable title has been changed
4299 * @param {Roo.bootstrap.Modal} this
4300 * @param {Roo.EventObject} value
4302 "titlechanged" : true
4305 this.buttons = this.buttons || [];
4308 this.tmpl = Roo.factory(this.tmpl);
4313 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4315 title : 'test dialog',
4325 specificTitle: false,
4327 buttonPosition: 'right',
4349 editableTitle : false,
4351 onRender : function(ct, position)
4353 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4356 var cfg = Roo.apply({}, this.getAutoCreate());
4359 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4361 //if (!cfg.name.length) {
4365 cfg.cls += ' ' + this.cls;
4368 cfg.style = this.style;
4370 this.el = Roo.get(document.body).createChild(cfg, position);
4372 //var type = this.el.dom.type;
4375 if(this.tabIndex !== undefined){
4376 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4379 this.dialogEl = this.el.select('.modal-dialog',true).first();
4380 this.bodyEl = this.el.select('.modal-body',true).first();
4381 this.closeEl = this.el.select('.modal-header .close', true).first();
4382 this.headerEl = this.el.select('.modal-header',true).first();
4383 this.titleEl = this.el.select('.modal-title',true).first();
4384 this.footerEl = this.el.select('.modal-footer',true).first();
4386 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4388 //this.el.addClass("x-dlg-modal");
4390 if (this.buttons.length) {
4391 Roo.each(this.buttons, function(bb) {
4392 var b = Roo.apply({}, bb);
4393 b.xns = b.xns || Roo.bootstrap;
4394 b.xtype = b.xtype || 'Button';
4395 if (typeof(b.listeners) == 'undefined') {
4396 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4399 var btn = Roo.factory(b);
4401 btn.render(this.getButtonContainer());
4405 // render the children.
4408 if(typeof(this.items) != 'undefined'){
4409 var items = this.items;
4412 for(var i =0;i < items.length;i++) {
4413 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4417 this.items = nitems;
4419 // where are these used - they used to be body/close/footer
4423 //this.el.addClass([this.fieldClass, this.cls]);
4427 getAutoCreate : function()
4429 // we will default to modal-body-overflow - might need to remove or make optional later.
4431 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4432 html : this.html || ''
4437 cls : 'modal-title',
4441 if(this.specificTitle){ // WTF is this?
4446 if (this.allow_close && Roo.bootstrap.version == 3) {
4456 if (this.editableTitle) {
4458 cls: 'form-control roo-editable-title d-none',
4464 if (this.allow_close && Roo.bootstrap.version == 4) {
4474 if(this.size.length){
4475 size = 'modal-' + this.size;
4478 var footer = Roo.bootstrap.version == 3 ?
4480 cls : 'modal-footer',
4484 cls: 'btn-' + this.buttonPosition
4489 { // BS4 uses mr-auto on left buttons....
4490 cls : 'modal-footer'
4501 cls: "modal-dialog " + size,
4504 cls : "modal-content",
4507 cls : 'modal-header',
4522 modal.cls += ' fade';
4528 getChildContainer : function() {
4533 getButtonContainer : function() {
4535 return Roo.bootstrap.version == 4 ?
4536 this.el.select('.modal-footer',true).first()
4537 : this.el.select('.modal-footer div',true).first();
4540 initEvents : function()
4542 if (this.allow_close) {
4543 this.closeEl.on('click', this.hide, this);
4545 Roo.EventManager.onWindowResize(this.resize, this, true);
4546 if (this.editableTitle) {
4547 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4548 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4549 this.headerEditEl.on('keyup', function(e) {
4550 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4551 this.toggleHeaderInput(false)
4554 this.headerEditEl.on('blur', function(e) {
4555 this.toggleHeaderInput(false)
4564 this.maskEl.setSize(
4565 Roo.lib.Dom.getViewWidth(true),
4566 Roo.lib.Dom.getViewHeight(true)
4569 if (this.fitwindow) {
4571 this.dialogEl.setStyle( { 'max-width' : '100%' });
4573 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4574 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4579 if(this.max_width !== 0) {
4581 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4584 this.setSize(w, this.height);
4588 if(this.max_height) {
4589 this.setSize(w,Math.min(
4591 Roo.lib.Dom.getViewportHeight(true) - 60
4597 if(!this.fit_content) {
4598 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4602 this.setSize(w, Math.min(
4604 this.headerEl.getHeight() +
4605 this.footerEl.getHeight() +
4606 this.getChildHeight(this.bodyEl.dom.childNodes),
4607 Roo.lib.Dom.getViewportHeight(true) - 60)
4613 setSize : function(w,h)
4624 if (!this.rendered) {
4627 this.toggleHeaderInput(false);
4628 //this.el.setStyle('display', 'block');
4629 this.el.removeClass('hideing');
4630 this.el.dom.style.display='block';
4632 Roo.get(document.body).addClass('modal-open');
4634 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4637 this.el.addClass('show');
4638 this.el.addClass('in');
4641 this.el.addClass('show');
4642 this.el.addClass('in');
4645 // not sure how we can show data in here..
4647 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4650 Roo.get(document.body).addClass("x-body-masked");
4652 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4653 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4654 this.maskEl.dom.style.display = 'block';
4655 this.maskEl.addClass('show');
4660 this.fireEvent('show', this);
4662 // set zindex here - otherwise it appears to be ignored...
4663 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4666 this.items.forEach( function(e) {
4667 e.layout ? e.layout() : false;
4675 if(this.fireEvent("beforehide", this) !== false){
4677 this.maskEl.removeClass('show');
4679 this.maskEl.dom.style.display = '';
4680 Roo.get(document.body).removeClass("x-body-masked");
4681 this.el.removeClass('in');
4682 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4684 if(this.animate){ // why
4685 this.el.addClass('hideing');
4686 this.el.removeClass('show');
4688 if (!this.el.hasClass('hideing')) {
4689 return; // it's been shown again...
4692 this.el.dom.style.display='';
4694 Roo.get(document.body).removeClass('modal-open');
4695 this.el.removeClass('hideing');
4699 this.el.removeClass('show');
4700 this.el.dom.style.display='';
4701 Roo.get(document.body).removeClass('modal-open');
4704 this.fireEvent('hide', this);
4707 isVisible : function()
4710 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4714 addButton : function(str, cb)
4718 var b = Roo.apply({}, { html : str } );
4719 b.xns = b.xns || Roo.bootstrap;
4720 b.xtype = b.xtype || 'Button';
4721 if (typeof(b.listeners) == 'undefined') {
4722 b.listeners = { click : cb.createDelegate(this) };
4725 var btn = Roo.factory(b);
4727 btn.render(this.getButtonContainer());
4733 setDefaultButton : function(btn)
4735 //this.el.select('.modal-footer').()
4738 resizeTo: function(w,h)
4740 this.dialogEl.setWidth(w);
4742 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4744 this.bodyEl.setHeight(h - diff);
4746 this.fireEvent('resize', this);
4749 setContentSize : function(w, h)
4753 onButtonClick: function(btn,e)
4756 this.fireEvent('btnclick', btn.name, e);
4759 * Set the title of the Dialog
4760 * @param {String} str new Title
4762 setTitle: function(str) {
4763 this.titleEl.dom.innerHTML = str;
4767 * Set the body of the Dialog
4768 * @param {String} str new Title
4770 setBody: function(str) {
4771 this.bodyEl.dom.innerHTML = str;
4774 * Set the body of the Dialog using the template
4775 * @param {Obj} data - apply this data to the template and replace the body contents.
4777 applyBody: function(obj)
4780 Roo.log("Error - using apply Body without a template");
4783 this.tmpl.overwrite(this.bodyEl, obj);
4786 getChildHeight : function(child_nodes)
4790 child_nodes.length == 0
4795 var child_height = 0;
4797 for(var i = 0; i < child_nodes.length; i++) {
4800 * for modal with tabs...
4801 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4803 var layout_childs = child_nodes[i].childNodes;
4805 for(var j = 0; j < layout_childs.length; j++) {
4807 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4809 var layout_body_childs = layout_childs[j].childNodes;
4811 for(var k = 0; k < layout_body_childs.length; k++) {
4813 if(layout_body_childs[k].classList.contains('navbar')) {
4814 child_height += layout_body_childs[k].offsetHeight;
4818 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4820 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4822 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4824 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4825 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4840 child_height += child_nodes[i].offsetHeight;
4841 // Roo.log(child_nodes[i].offsetHeight);
4844 return child_height;
4846 toggleHeaderInput : function(is_edit)
4848 if (!this.editableTitle) {
4849 return; // not editable.
4851 if (is_edit && this.is_header_editing) {
4852 return; // already editing..
4856 this.headerEditEl.dom.value = this.title;
4857 this.headerEditEl.removeClass('d-none');
4858 this.headerEditEl.dom.focus();
4859 this.titleEl.addClass('d-none');
4861 this.is_header_editing = true;
4864 // flip back to not editing.
4865 this.title = this.headerEditEl.dom.value;
4866 this.headerEditEl.addClass('d-none');
4867 this.titleEl.removeClass('d-none');
4868 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4869 this.is_header_editing = false;
4870 this.fireEvent('titlechanged', this, this.title);
4879 Roo.apply(Roo.bootstrap.Modal, {
4881 * Button config that displays a single OK button
4890 * Button config that displays Yes and No buttons
4906 * Button config that displays OK and Cancel buttons
4921 * Button config that displays Yes, No and Cancel buttons
4946 * messagebox - can be used as a replace
4950 * @class Roo.MessageBox
4951 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4955 Roo.Msg.alert('Status', 'Changes saved successfully.');
4957 // Prompt for user data:
4958 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4960 // process text value...
4964 // Show a dialog using config options:
4966 title:'Save Changes?',
4967 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4968 buttons: Roo.Msg.YESNOCANCEL,
4975 Roo.bootstrap.MessageBox = function(){
4976 var dlg, opt, mask, waitTimer;
4977 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4978 var buttons, activeTextEl, bwidth;
4982 var handleButton = function(button){
4984 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4988 var handleHide = function(){
4990 dlg.el.removeClass(opt.cls);
4993 // Roo.TaskMgr.stop(waitTimer);
4994 // waitTimer = null;
4999 var updateButtons = function(b){
5002 buttons["ok"].hide();
5003 buttons["cancel"].hide();
5004 buttons["yes"].hide();
5005 buttons["no"].hide();
5006 dlg.footerEl.hide();
5010 dlg.footerEl.show();
5011 for(var k in buttons){
5012 if(typeof buttons[k] != "function"){
5015 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5016 width += buttons[k].el.getWidth()+15;
5026 var handleEsc = function(d, k, e){
5027 if(opt && opt.closable !== false){
5037 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5038 * @return {Roo.BasicDialog} The BasicDialog element
5040 getDialog : function(){
5042 dlg = new Roo.bootstrap.Modal( {
5045 //constraintoviewport:false,
5047 //collapsible : false,
5052 //buttonAlign:"center",
5053 closeClick : function(){
5054 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5057 handleButton("cancel");
5062 dlg.on("hide", handleHide);
5064 //dlg.addKeyListener(27, handleEsc);
5066 this.buttons = buttons;
5067 var bt = this.buttonText;
5068 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5069 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5070 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5071 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5073 bodyEl = dlg.bodyEl.createChild({
5075 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5076 '<textarea class="roo-mb-textarea"></textarea>' +
5077 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5079 msgEl = bodyEl.dom.firstChild;
5080 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5081 textboxEl.enableDisplayMode();
5082 textboxEl.addKeyListener([10,13], function(){
5083 if(dlg.isVisible() && opt && opt.buttons){
5086 }else if(opt.buttons.yes){
5087 handleButton("yes");
5091 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5092 textareaEl.enableDisplayMode();
5093 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5094 progressEl.enableDisplayMode();
5096 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5097 var pf = progressEl.dom.firstChild;
5099 pp = Roo.get(pf.firstChild);
5100 pp.setHeight(pf.offsetHeight);
5108 * Updates the message box body text
5109 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5110 * the XHTML-compliant non-breaking space character '&#160;')
5111 * @return {Roo.MessageBox} This message box
5113 updateText : function(text)
5115 if(!dlg.isVisible() && !opt.width){
5116 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5117 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5119 msgEl.innerHTML = text || ' ';
5121 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5122 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5124 Math.min(opt.width || cw , this.maxWidth),
5125 Math.max(opt.minWidth || this.minWidth, bwidth)
5128 activeTextEl.setWidth(w);
5130 if(dlg.isVisible()){
5131 dlg.fixedcenter = false;
5133 // to big, make it scroll. = But as usual stupid IE does not support
5136 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5137 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5138 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5140 bodyEl.dom.style.height = '';
5141 bodyEl.dom.style.overflowY = '';
5144 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5146 bodyEl.dom.style.overflowX = '';
5149 dlg.setContentSize(w, bodyEl.getHeight());
5150 if(dlg.isVisible()){
5151 dlg.fixedcenter = true;
5157 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5158 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5159 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5160 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5161 * @return {Roo.MessageBox} This message box
5163 updateProgress : function(value, text){
5165 this.updateText(text);
5168 if (pp) { // weird bug on my firefox - for some reason this is not defined
5169 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5170 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5176 * Returns true if the message box is currently displayed
5177 * @return {Boolean} True if the message box is visible, else false
5179 isVisible : function(){
5180 return dlg && dlg.isVisible();
5184 * Hides the message box if it is displayed
5187 if(this.isVisible()){
5193 * Displays a new message box, or reinitializes an existing message box, based on the config options
5194 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5195 * The following config object properties are supported:
5197 Property Type Description
5198 ---------- --------------- ------------------------------------------------------------------------------------
5199 animEl String/Element An id or Element from which the message box should animate as it opens and
5200 closes (defaults to undefined)
5201 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5202 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5203 closable Boolean False to hide the top-right close button (defaults to true). Note that
5204 progress and wait dialogs will ignore this property and always hide the
5205 close button as they can only be closed programmatically.
5206 cls String A custom CSS class to apply to the message box element
5207 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5208 displayed (defaults to 75)
5209 fn Function A callback function to execute after closing the dialog. The arguments to the
5210 function will be btn (the name of the button that was clicked, if applicable,
5211 e.g. "ok"), and text (the value of the active text field, if applicable).
5212 Progress and wait dialogs will ignore this option since they do not respond to
5213 user actions and can only be closed programmatically, so any required function
5214 should be called by the same code after it closes the dialog.
5215 icon String A CSS class that provides a background image to be used as an icon for
5216 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5217 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5218 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5219 modal Boolean False to allow user interaction with the page while the message box is
5220 displayed (defaults to true)
5221 msg String A string that will replace the existing message box body text (defaults
5222 to the XHTML-compliant non-breaking space character ' ')
5223 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5224 progress Boolean True to display a progress bar (defaults to false)
5225 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5226 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5227 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5228 title String The title text
5229 value String The string value to set into the active textbox element if displayed
5230 wait Boolean True to display a progress bar (defaults to false)
5231 width Number The width of the dialog in pixels
5238 msg: 'Please enter your address:',
5240 buttons: Roo.MessageBox.OKCANCEL,
5243 animEl: 'addAddressBtn'
5246 * @param {Object} config Configuration options
5247 * @return {Roo.MessageBox} This message box
5249 show : function(options)
5252 // this causes nightmares if you show one dialog after another
5253 // especially on callbacks..
5255 if(this.isVisible()){
5258 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5259 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5260 Roo.log("New Dialog Message:" + options.msg )
5261 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5262 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5265 var d = this.getDialog();
5267 d.setTitle(opt.title || " ");
5268 d.closeEl.setDisplayed(opt.closable !== false);
5269 activeTextEl = textboxEl;
5270 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5275 textareaEl.setHeight(typeof opt.multiline == "number" ?
5276 opt.multiline : this.defaultTextHeight);
5277 activeTextEl = textareaEl;
5286 progressEl.setDisplayed(opt.progress === true);
5288 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5290 this.updateProgress(0);
5291 activeTextEl.dom.value = opt.value || "";
5293 dlg.setDefaultButton(activeTextEl);
5295 var bs = opt.buttons;
5299 }else if(bs && bs.yes){
5300 db = buttons["yes"];
5302 dlg.setDefaultButton(db);
5304 bwidth = updateButtons(opt.buttons);
5305 this.updateText(opt.msg);
5307 d.el.addClass(opt.cls);
5309 d.proxyDrag = opt.proxyDrag === true;
5310 d.modal = opt.modal !== false;
5311 d.mask = opt.modal !== false ? mask : false;
5313 // force it to the end of the z-index stack so it gets a cursor in FF
5314 document.body.appendChild(dlg.el.dom);
5315 d.animateTarget = null;
5316 d.show(options.animEl);
5322 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5323 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5324 * and closing the message box when the process is complete.
5325 * @param {String} title The title bar text
5326 * @param {String} msg The message box body text
5327 * @return {Roo.MessageBox} This message box
5329 progress : function(title, msg){
5336 minWidth: this.minProgressWidth,
5343 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5344 * If a callback function is passed it will be called after the user clicks the button, and the
5345 * id of the button that was clicked will be passed as the only parameter to the callback
5346 * (could also be the top-right close button).
5347 * @param {String} title The title bar text
5348 * @param {String} msg The message box body text
5349 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5350 * @param {Object} scope (optional) The scope of the callback function
5351 * @return {Roo.MessageBox} This message box
5353 alert : function(title, msg, fn, scope)
5368 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5369 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5370 * You are responsible for closing the message box when the process is complete.
5371 * @param {String} msg The message box body text
5372 * @param {String} title (optional) The title bar text
5373 * @return {Roo.MessageBox} This message box
5375 wait : function(msg, title){
5386 waitTimer = Roo.TaskMgr.start({
5388 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5396 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5397 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5398 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5399 * @param {String} title The title bar text
5400 * @param {String} msg The message box body text
5401 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5402 * @param {Object} scope (optional) The scope of the callback function
5403 * @return {Roo.MessageBox} This message box
5405 confirm : function(title, msg, fn, scope){
5409 buttons: this.YESNO,
5418 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5419 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5420 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5421 * (could also be the top-right close button) and the text that was entered will be passed as the two
5422 * parameters to the callback.
5423 * @param {String} title The title bar text
5424 * @param {String} msg The message box body text
5425 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5426 * @param {Object} scope (optional) The scope of the callback function
5427 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5428 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5429 * @return {Roo.MessageBox} This message box
5431 prompt : function(title, msg, fn, scope, multiline){
5435 buttons: this.OKCANCEL,
5440 multiline: multiline,
5447 * Button config that displays a single OK button
5452 * Button config that displays Yes and No buttons
5455 YESNO : {yes:true, no:true},
5457 * Button config that displays OK and Cancel buttons
5460 OKCANCEL : {ok:true, cancel:true},
5462 * Button config that displays Yes, No and Cancel buttons
5465 YESNOCANCEL : {yes:true, no:true, cancel:true},
5468 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5471 defaultTextHeight : 75,
5473 * The maximum width in pixels of the message box (defaults to 600)
5478 * The minimum width in pixels of the message box (defaults to 100)
5483 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5484 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5487 minProgressWidth : 250,
5489 * An object containing the default button text strings that can be overriden for localized language support.
5490 * Supported properties are: ok, cancel, yes and no.
5491 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5504 * Shorthand for {@link Roo.MessageBox}
5506 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5507 Roo.Msg = Roo.Msg || Roo.MessageBox;
5516 * @class Roo.bootstrap.Navbar
5517 * @extends Roo.bootstrap.Component
5518 * Bootstrap Navbar class
5521 * Create a new Navbar
5522 * @param {Object} config The config object
5526 Roo.bootstrap.Navbar = function(config){
5527 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5531 * @event beforetoggle
5532 * Fire before toggle the menu
5533 * @param {Roo.EventObject} e
5535 "beforetoggle" : true
5539 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5548 getAutoCreate : function(){
5551 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5555 initEvents :function ()
5557 //Roo.log(this.el.select('.navbar-toggle',true));
5558 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5565 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5567 var size = this.el.getSize();
5568 this.maskEl.setSize(size.width, size.height);
5569 this.maskEl.enableDisplayMode("block");
5578 getChildContainer : function()
5580 if (this.el && this.el.select('.collapse').getCount()) {
5581 return this.el.select('.collapse',true).first();
5596 onToggle : function()
5599 if(this.fireEvent('beforetoggle', this) === false){
5602 var ce = this.el.select('.navbar-collapse',true).first();
5604 if (!ce.hasClass('show')) {
5614 * Expand the navbar pulldown
5616 expand : function ()
5619 var ce = this.el.select('.navbar-collapse',true).first();
5620 if (ce.hasClass('collapsing')) {
5623 ce.dom.style.height = '';
5625 ce.addClass('in'); // old...
5626 ce.removeClass('collapse');
5627 ce.addClass('show');
5628 var h = ce.getHeight();
5630 ce.removeClass('show');
5631 // at this point we should be able to see it..
5632 ce.addClass('collapsing');
5634 ce.setHeight(0); // resize it ...
5635 ce.on('transitionend', function() {
5636 //Roo.log('done transition');
5637 ce.removeClass('collapsing');
5638 ce.addClass('show');
5639 ce.removeClass('collapse');
5641 ce.dom.style.height = '';
5642 }, this, { single: true} );
5644 ce.dom.scrollTop = 0;
5647 * Collapse the navbar pulldown
5649 collapse : function()
5651 var ce = this.el.select('.navbar-collapse',true).first();
5653 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5654 // it's collapsed or collapsing..
5657 ce.removeClass('in'); // old...
5658 ce.setHeight(ce.getHeight());
5659 ce.removeClass('show');
5660 ce.addClass('collapsing');
5662 ce.on('transitionend', function() {
5663 ce.dom.style.height = '';
5664 ce.removeClass('collapsing');
5665 ce.addClass('collapse');
5666 }, this, { single: true} );
5686 * @class Roo.bootstrap.NavSimplebar
5687 * @extends Roo.bootstrap.Navbar
5688 * Bootstrap Sidebar class
5690 * @cfg {Boolean} inverse is inverted color
5692 * @cfg {String} type (nav | pills | tabs)
5693 * @cfg {Boolean} arrangement stacked | justified
5694 * @cfg {String} align (left | right) alignment
5696 * @cfg {Boolean} main (true|false) main nav bar? default false
5697 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5699 * @cfg {String} tag (header|footer|nav|div) default is nav
5701 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5705 * Create a new Sidebar
5706 * @param {Object} config The config object
5710 Roo.bootstrap.NavSimplebar = function(config){
5711 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5714 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5730 getAutoCreate : function(){
5734 tag : this.tag || 'div',
5735 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5737 if (['light','white'].indexOf(this.weight) > -1) {
5738 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5740 cfg.cls += ' bg-' + this.weight;
5743 cfg.cls += ' navbar-inverse';
5747 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5749 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5758 cls: 'nav nav-' + this.xtype,
5764 this.type = this.type || 'nav';
5765 if (['tabs','pills'].indexOf(this.type) != -1) {
5766 cfg.cn[0].cls += ' nav-' + this.type
5770 if (this.type!=='nav') {
5771 Roo.log('nav type must be nav/tabs/pills')
5773 cfg.cn[0].cls += ' navbar-nav'
5779 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5780 cfg.cn[0].cls += ' nav-' + this.arrangement;
5784 if (this.align === 'right') {
5785 cfg.cn[0].cls += ' navbar-right';
5810 * navbar-expand-md fixed-top
5814 * @class Roo.bootstrap.NavHeaderbar
5815 * @extends Roo.bootstrap.NavSimplebar
5816 * Bootstrap Sidebar class
5818 * @cfg {String} brand what is brand
5819 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5820 * @cfg {String} brand_href href of the brand
5821 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5822 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5823 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5824 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5827 * Create a new Sidebar
5828 * @param {Object} config The config object
5832 Roo.bootstrap.NavHeaderbar = function(config){
5833 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5837 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5844 desktopCenter : false,
5847 getAutoCreate : function(){
5850 tag: this.nav || 'nav',
5851 cls: 'navbar navbar-expand-md',
5857 if (this.desktopCenter) {
5858 cn.push({cls : 'container', cn : []});
5866 cls: 'navbar-toggle navbar-toggler',
5867 'data-toggle': 'collapse',
5872 html: 'Toggle navigation'
5876 cls: 'icon-bar navbar-toggler-icon'
5889 cn.push( Roo.bootstrap.version == 4 ? btn : {
5891 cls: 'navbar-header',
5900 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5904 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5906 if (['light','white'].indexOf(this.weight) > -1) {
5907 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5909 cfg.cls += ' bg-' + this.weight;
5912 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5913 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5915 // tag can override this..
5917 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5920 if (this.brand !== '') {
5921 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5922 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5924 href: this.brand_href ? this.brand_href : '#',
5925 cls: 'navbar-brand',
5933 cfg.cls += ' main-nav';
5941 getHeaderChildContainer : function()
5943 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5944 return this.el.select('.navbar-header',true).first();
5947 return this.getChildContainer();
5950 getChildContainer : function()
5953 return this.el.select('.roo-navbar-collapse',true).first();
5958 initEvents : function()
5960 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5962 if (this.autohide) {
5967 Roo.get(document).on('scroll',function(e) {
5968 var ns = Roo.get(document).getScroll().top;
5969 var os = prevScroll;
5973 ft.removeClass('slideDown');
5974 ft.addClass('slideUp');
5977 ft.removeClass('slideUp');
5978 ft.addClass('slideDown');
5999 * @class Roo.bootstrap.NavSidebar
6000 * @extends Roo.bootstrap.Navbar
6001 * Bootstrap Sidebar class
6004 * Create a new Sidebar
6005 * @param {Object} config The config object
6009 Roo.bootstrap.NavSidebar = function(config){
6010 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6013 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6015 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6017 getAutoCreate : function(){
6022 cls: 'sidebar sidebar-nav'
6044 * @class Roo.bootstrap.NavGroup
6045 * @extends Roo.bootstrap.Component
6046 * Bootstrap NavGroup class
6047 * @cfg {String} align (left|right)
6048 * @cfg {Boolean} inverse
6049 * @cfg {String} type (nav|pills|tab) default nav
6050 * @cfg {String} navId - reference Id for navbar.
6051 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6054 * Create a new nav group
6055 * @param {Object} config The config object
6058 Roo.bootstrap.NavGroup = function(config){
6059 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6062 Roo.bootstrap.NavGroup.register(this);
6066 * Fires when the active item changes
6067 * @param {Roo.bootstrap.NavGroup} this
6068 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6069 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6076 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6088 getAutoCreate : function()
6090 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6096 if (Roo.bootstrap.version == 4) {
6097 if (['tabs','pills'].indexOf(this.type) != -1) {
6098 cfg.cls += ' nav-' + this.type;
6100 // trying to remove so header bar can right align top?
6101 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6102 // do not use on header bar...
6103 cfg.cls += ' navbar-nav';
6108 if (['tabs','pills'].indexOf(this.type) != -1) {
6109 cfg.cls += ' nav-' + this.type
6111 if (this.type !== 'nav') {
6112 Roo.log('nav type must be nav/tabs/pills')
6114 cfg.cls += ' navbar-nav'
6118 if (this.parent() && this.parent().sidebar) {
6121 cls: 'dashboard-menu sidebar-menu'
6127 if (this.form === true) {
6130 cls: 'navbar-form form-inline'
6132 //nav navbar-right ml-md-auto
6133 if (this.align === 'right') {
6134 cfg.cls += ' navbar-right ml-md-auto';
6136 cfg.cls += ' navbar-left';
6140 if (this.align === 'right') {
6141 cfg.cls += ' navbar-right ml-md-auto';
6143 cfg.cls += ' mr-auto';
6147 cfg.cls += ' navbar-inverse';
6155 * sets the active Navigation item
6156 * @param {Roo.bootstrap.NavItem} the new current navitem
6158 setActiveItem : function(item)
6161 Roo.each(this.navItems, function(v){
6166 v.setActive(false, true);
6173 item.setActive(true, true);
6174 this.fireEvent('changed', this, item, prev);
6179 * gets the active Navigation item
6180 * @return {Roo.bootstrap.NavItem} the current navitem
6182 getActive : function()
6186 Roo.each(this.navItems, function(v){
6197 indexOfNav : function()
6201 Roo.each(this.navItems, function(v,i){
6212 * adds a Navigation item
6213 * @param {Roo.bootstrap.NavItem} the navitem to add
6215 addItem : function(cfg)
6217 if (this.form && Roo.bootstrap.version == 4) {
6220 var cn = new Roo.bootstrap.NavItem(cfg);
6222 cn.parentId = this.id;
6223 cn.onRender(this.el, null);
6227 * register a Navigation item
6228 * @param {Roo.bootstrap.NavItem} the navitem to add
6230 register : function(item)
6232 this.navItems.push( item);
6233 item.navId = this.navId;
6238 * clear all the Navigation item
6241 clearAll : function()
6244 this.el.dom.innerHTML = '';
6247 getNavItem: function(tabId)
6250 Roo.each(this.navItems, function(e) {
6251 if (e.tabId == tabId) {
6261 setActiveNext : function()
6263 var i = this.indexOfNav(this.getActive());
6264 if (i > this.navItems.length) {
6267 this.setActiveItem(this.navItems[i+1]);
6269 setActivePrev : function()
6271 var i = this.indexOfNav(this.getActive());
6275 this.setActiveItem(this.navItems[i-1]);
6277 clearWasActive : function(except) {
6278 Roo.each(this.navItems, function(e) {
6279 if (e.tabId != except.tabId && e.was_active) {
6280 e.was_active = false;
6287 getWasActive : function ()
6290 Roo.each(this.navItems, function(e) {
6305 Roo.apply(Roo.bootstrap.NavGroup, {
6309 * register a Navigation Group
6310 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6312 register : function(navgrp)
6314 this.groups[navgrp.navId] = navgrp;
6318 * fetch a Navigation Group based on the navigation ID
6319 * @param {string} the navgroup to add
6320 * @returns {Roo.bootstrap.NavGroup} the navgroup
6322 get: function(navId) {
6323 if (typeof(this.groups[navId]) == 'undefined') {
6325 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6327 return this.groups[navId] ;
6342 * @class Roo.bootstrap.NavItem
6343 * @extends Roo.bootstrap.Component
6344 * Bootstrap Navbar.NavItem class
6345 * @cfg {String} href link to
6346 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6347 * @cfg {Boolean} button_outline show and outlined button
6348 * @cfg {String} html content of button
6349 * @cfg {String} badge text inside badge
6350 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6351 * @cfg {String} glyphicon DEPRICATED - use fa
6352 * @cfg {String} icon DEPRICATED - use fa
6353 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6354 * @cfg {Boolean} active Is item active
6355 * @cfg {Boolean} disabled Is item disabled
6356 * @cfg {String} linkcls Link Class
6357 * @cfg {Boolean} preventDefault (true | false) default false
6358 * @cfg {String} tabId the tab that this item activates.
6359 * @cfg {String} tagtype (a|span) render as a href or span?
6360 * @cfg {Boolean} animateRef (true|false) link to element default false
6363 * Create a new Navbar Item
6364 * @param {Object} config The config object
6366 Roo.bootstrap.NavItem = function(config){
6367 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6372 * The raw click event for the entire grid.
6373 * @param {Roo.EventObject} e
6378 * Fires when the active item active state changes
6379 * @param {Roo.bootstrap.NavItem} this
6380 * @param {boolean} state the new state
6386 * Fires when scroll to element
6387 * @param {Roo.bootstrap.NavItem} this
6388 * @param {Object} options
6389 * @param {Roo.EventObject} e
6397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6406 preventDefault : false,
6414 button_outline : false,
6418 getAutoCreate : function(){
6425 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6428 cfg.cls += ' active' ;
6430 if (this.disabled) {
6431 cfg.cls += ' disabled';
6435 if (this.button_weight.length) {
6436 cfg.tag = this.href ? 'a' : 'button';
6437 cfg.html = this.html || '';
6438 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6440 cfg.href = this.href;
6443 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6445 cfg.cls += " nav-html";
6448 // menu .. should add dropdown-menu class - so no need for carat..
6450 if (this.badge !== '') {
6452 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6457 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6461 href : this.href || "#",
6462 html: this.html || '',
6466 if (this.tagtype == 'a') {
6467 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6471 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6472 } else if (this.fa) {
6473 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6474 } else if(this.glyphicon) {
6475 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6477 cfg.cn[0].cls += " nav-html";
6481 cfg.cn[0].html += " <span class='caret'></span>";
6485 if (this.badge !== '') {
6486 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6494 onRender : function(ct, position)
6496 // Roo.log("Call onRender: " + this.xtype);
6497 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6501 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6502 this.navLink = this.el.select('.nav-link',true).first();
6503 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6508 initEvents: function()
6510 if (typeof (this.menu) != 'undefined') {
6511 this.menu.parentType = this.xtype;
6512 this.menu.triggerEl = this.el;
6513 this.menu = this.addxtype(Roo.apply({}, this.menu));
6516 this.el.on('click', this.onClick, this);
6518 //if(this.tagtype == 'span'){
6519 // this.el.select('span',true).on('click', this.onClick, this);
6522 // at this point parent should be available..
6523 this.parent().register(this);
6526 onClick : function(e)
6528 if (e.getTarget('.dropdown-menu-item')) {
6529 // did you click on a menu itemm.... - then don't trigger onclick..
6534 this.preventDefault ||
6537 Roo.log("NavItem - prevent Default?");
6541 if (this.disabled) {
6545 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6546 if (tg && tg.transition) {
6547 Roo.log("waiting for the transitionend");
6553 //Roo.log("fire event clicked");
6554 if(this.fireEvent('click', this, e) === false){
6558 if(this.tagtype == 'span'){
6562 //Roo.log(this.href);
6563 var ael = this.el.select('a',true).first();
6566 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6567 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6568 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6569 return; // ignore... - it's a 'hash' to another page.
6571 Roo.log("NavItem - prevent Default?");
6573 this.scrollToElement(e);
6577 var p = this.parent();
6579 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6580 if (typeof(p.setActiveItem) !== 'undefined') {
6581 p.setActiveItem(this);
6585 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6586 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6587 // remove the collapsed menu expand...
6588 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6592 isActive: function () {
6595 setActive : function(state, fire, is_was_active)
6597 if (this.active && !state && this.navId) {
6598 this.was_active = true;
6599 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6601 nv.clearWasActive(this);
6605 this.active = state;
6608 this.el.removeClass('active');
6609 this.navLink ? this.navLink.removeClass('active') : false;
6610 } else if (!this.el.hasClass('active')) {
6612 this.el.addClass('active');
6613 if (Roo.bootstrap.version == 4 && this.navLink ) {
6614 this.navLink.addClass('active');
6619 this.fireEvent('changed', this, state);
6622 // show a panel if it's registered and related..
6624 if (!this.navId || !this.tabId || !state || is_was_active) {
6628 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6632 var pan = tg.getPanelByName(this.tabId);
6636 // if we can not flip to new panel - go back to old nav highlight..
6637 if (false == tg.showPanel(pan)) {
6638 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6640 var onav = nv.getWasActive();
6642 onav.setActive(true, false, true);
6651 // this should not be here...
6652 setDisabled : function(state)
6654 this.disabled = state;
6656 this.el.removeClass('disabled');
6657 } else if (!this.el.hasClass('disabled')) {
6658 this.el.addClass('disabled');
6664 * Fetch the element to display the tooltip on.
6665 * @return {Roo.Element} defaults to this.el
6667 tooltipEl : function()
6669 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6672 scrollToElement : function(e)
6674 var c = document.body;
6677 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6679 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6680 c = document.documentElement;
6683 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6689 var o = target.calcOffsetsTo(c);
6696 this.fireEvent('scrollto', this, options, e);
6698 Roo.get(c).scrollTo('top', options.value, true);
6703 * Set the HTML (text content) of the item
6704 * @param {string} html content for the nav item
6706 setHtml : function(html)
6709 this.htmlEl.dom.innerHTML = html;
6721 * <span> icon </span>
6722 * <span> text </span>
6723 * <span>badge </span>
6727 * @class Roo.bootstrap.NavSidebarItem
6728 * @extends Roo.bootstrap.NavItem
6729 * Bootstrap Navbar.NavSidebarItem class
6730 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6731 * {Boolean} open is the menu open
6732 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6733 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6734 * {String} buttonSize (sm|md|lg)the extra classes for the button
6735 * {Boolean} showArrow show arrow next to the text (default true)
6737 * Create a new Navbar Button
6738 * @param {Object} config The config object
6740 Roo.bootstrap.NavSidebarItem = function(config){
6741 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6746 * The raw click event for the entire grid.
6747 * @param {Roo.EventObject} e
6752 * Fires when the active item active state changes
6753 * @param {Roo.bootstrap.NavSidebarItem} this
6754 * @param {boolean} state the new state
6762 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6764 badgeWeight : 'default',
6770 buttonWeight : 'default',
6776 getAutoCreate : function(){
6781 href : this.href || '#',
6787 if(this.buttonView){
6790 href : this.href || '#',
6791 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6804 cfg.cls += ' active';
6807 if (this.disabled) {
6808 cfg.cls += ' disabled';
6811 cfg.cls += ' open x-open';
6814 if (this.glyphicon || this.icon) {
6815 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6816 a.cn.push({ tag : 'i', cls : c }) ;
6819 if(!this.buttonView){
6822 html : this.html || ''
6829 if (this.badge !== '') {
6830 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6836 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6839 a.cls += ' dropdown-toggle treeview' ;
6845 initEvents : function()
6847 if (typeof (this.menu) != 'undefined') {
6848 this.menu.parentType = this.xtype;
6849 this.menu.triggerEl = this.el;
6850 this.menu = this.addxtype(Roo.apply({}, this.menu));
6853 this.el.on('click', this.onClick, this);
6855 if(this.badge !== ''){
6856 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6861 onClick : function(e)
6868 if(this.preventDefault){
6872 this.fireEvent('click', this, e);
6875 disable : function()
6877 this.setDisabled(true);
6882 this.setDisabled(false);
6885 setDisabled : function(state)
6887 if(this.disabled == state){
6891 this.disabled = state;
6894 this.el.addClass('disabled');
6898 this.el.removeClass('disabled');
6903 setActive : function(state)
6905 if(this.active == state){
6909 this.active = state;
6912 this.el.addClass('active');
6916 this.el.removeClass('active');
6921 isActive: function ()
6926 setBadge : function(str)
6932 this.badgeEl.dom.innerHTML = str;
6947 Roo.namespace('Roo.bootstrap.breadcrumb');
6951 * @class Roo.bootstrap.breadcrumb.Nav
6952 * @extends Roo.bootstrap.Component
6953 * Bootstrap Breadcrumb Nav Class
6955 * @children Roo.bootstrap.breadcrumb.Item
6958 * Create a new breadcrumb.Nav
6959 * @param {Object} config The config object
6963 Roo.bootstrap.breadcrumb.Nav = function(config){
6964 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6969 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6971 getAutoCreate : function()
6988 initEvents: function()
6990 this.olEl = this.el.select('ol',true).first();
6992 getChildContainer : function()
7008 * @class Roo.bootstrap.breadcrumb.Nav
7009 * @extends Roo.bootstrap.Component
7010 * Bootstrap Breadcrumb Nav Class
7012 * @children Roo.bootstrap.breadcrumb.Component
7013 * @cfg {String} html the content of the link.
7014 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7015 * @cfg {Boolean} active is it active
7019 * Create a new breadcrumb.Nav
7020 * @param {Object} config The config object
7023 Roo.bootstrap.breadcrumb.Item = function(config){
7024 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7029 * The img click event for the img.
7030 * @param {Roo.EventObject} e
7037 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7042 getAutoCreate : function()
7047 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7049 if (this.href !== false) {
7056 cfg.html = this.html;
7062 initEvents: function()
7065 this.el.select('a', true).first().on('click',this.onClick, this)
7069 onClick : function(e)
7072 this.fireEvent('click',this, e);
7085 * @class Roo.bootstrap.Row
7086 * @extends Roo.bootstrap.Component
7087 * Bootstrap Row class (contains columns...)
7091 * @param {Object} config The config object
7094 Roo.bootstrap.Row = function(config){
7095 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7098 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7100 getAutoCreate : function(){
7119 * @class Roo.bootstrap.Pagination
7120 * @extends Roo.bootstrap.Component
7121 * Bootstrap Pagination class
7122 * @cfg {String} size xs | sm | md | lg
7123 * @cfg {Boolean} inverse false | true
7126 * Create a new Pagination
7127 * @param {Object} config The config object
7130 Roo.bootstrap.Pagination = function(config){
7131 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7134 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7140 getAutoCreate : function(){
7146 cfg.cls += ' inverse';
7152 cfg.cls += " " + this.cls;
7170 * @class Roo.bootstrap.PaginationItem
7171 * @extends Roo.bootstrap.Component
7172 * Bootstrap PaginationItem class
7173 * @cfg {String} html text
7174 * @cfg {String} href the link
7175 * @cfg {Boolean} preventDefault (true | false) default true
7176 * @cfg {Boolean} active (true | false) default false
7177 * @cfg {Boolean} disabled default false
7181 * Create a new PaginationItem
7182 * @param {Object} config The config object
7186 Roo.bootstrap.PaginationItem = function(config){
7187 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7192 * The raw click event for the entire grid.
7193 * @param {Roo.EventObject} e
7199 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7203 preventDefault: true,
7208 getAutoCreate : function(){
7214 href : this.href ? this.href : '#',
7215 html : this.html ? this.html : ''
7225 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7229 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7235 initEvents: function() {
7237 this.el.on('click', this.onClick, this);
7240 onClick : function(e)
7242 Roo.log('PaginationItem on click ');
7243 if(this.preventDefault){
7251 this.fireEvent('click', this, e);
7267 * @class Roo.bootstrap.Slider
7268 * @extends Roo.bootstrap.Component
7269 * Bootstrap Slider class
7272 * Create a new Slider
7273 * @param {Object} config The config object
7276 Roo.bootstrap.Slider = function(config){
7277 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7280 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7282 getAutoCreate : function(){
7286 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7290 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7302 * Ext JS Library 1.1.1
7303 * Copyright(c) 2006-2007, Ext JS, LLC.
7305 * Originally Released Under LGPL - original licence link has changed is not relivant.
7308 * <script type="text/javascript">
7311 * @extends Roo.dd.DDProxy
7312 * @class Roo.grid.SplitDragZone
7313 * Support for Column Header resizing
7315 * @param {Object} config
7318 // This is a support class used internally by the Grid components
7319 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7321 this.view = grid.getView();
7322 this.proxy = this.view.resizeProxy;
7323 Roo.grid.SplitDragZone.superclass.constructor.call(
7326 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7328 dragElId : Roo.id(this.proxy.dom),
7333 this.setHandleElId(Roo.id(hd));
7334 if (hd2 !== false) {
7335 this.setOuterHandleElId(Roo.id(hd2));
7338 this.scroll = false;
7340 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7341 fly: Roo.Element.fly,
7343 b4StartDrag : function(x, y){
7344 this.view.headersDisabled = true;
7345 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7346 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7348 this.proxy.setHeight(h);
7350 // for old system colWidth really stored the actual width?
7351 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7352 // which in reality did not work.. - it worked only for fixed sizes
7353 // for resizable we need to use actual sizes.
7354 var w = this.cm.getColumnWidth(this.cellIndex);
7355 if (!this.view.mainWrap) {
7357 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7362 // this was w-this.grid.minColumnWidth;
7363 // doesnt really make sense? - w = thie curren width or the rendered one?
7364 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7365 this.resetConstraints();
7366 this.setXConstraint(minw, 1000);
7367 this.setYConstraint(0, 0);
7368 this.minX = x - minw;
7369 this.maxX = x + 1000;
7371 if (!this.view.mainWrap) { // this is Bootstrap code..
7372 this.getDragEl().style.display='block';
7375 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7379 handleMouseDown : function(e){
7380 ev = Roo.EventObject.setEvent(e);
7381 var t = this.fly(ev.getTarget());
7382 if(t.hasClass("x-grid-split")){
7383 this.cellIndex = this.view.getCellIndex(t.dom);
7385 this.cm = this.grid.colModel;
7386 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7387 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7392 endDrag : function(e){
7393 this.view.headersDisabled = false;
7394 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7395 var diff = endX - this.startPos;
7397 var w = this.cm.getColumnWidth(this.cellIndex);
7398 if (!this.view.mainWrap) {
7401 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7404 autoOffset : function(){
7409 * Ext JS Library 1.1.1
7410 * Copyright(c) 2006-2007, Ext JS, LLC.
7412 * Originally Released Under LGPL - original licence link has changed is not relivant.
7415 * <script type="text/javascript">
7419 * @class Roo.grid.AbstractSelectionModel
7420 * @extends Roo.util.Observable
7421 * Abstract base class for grid SelectionModels. It provides the interface that should be
7422 * implemented by descendant classes. This class should not be directly instantiated.
7425 Roo.grid.AbstractSelectionModel = function(){
7426 this.locked = false;
7427 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7430 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7431 /** @ignore Called by the grid automatically. Do not call directly. */
7432 init : function(grid){
7438 * Locks the selections.
7445 * Unlocks the selections.
7447 unlock : function(){
7448 this.locked = false;
7452 * Returns true if the selections are locked.
7455 isLocked : function(){
7460 * Ext JS Library 1.1.1
7461 * Copyright(c) 2006-2007, Ext JS, LLC.
7463 * Originally Released Under LGPL - original licence link has changed is not relivant.
7466 * <script type="text/javascript">
7469 * @extends Roo.grid.AbstractSelectionModel
7470 * @class Roo.grid.RowSelectionModel
7471 * The default SelectionModel used by {@link Roo.grid.Grid}.
7472 * It supports multiple selections and keyboard selection/navigation.
7474 * @param {Object} config
7476 Roo.grid.RowSelectionModel = function(config){
7477 Roo.apply(this, config);
7478 this.selections = new Roo.util.MixedCollection(false, function(o){
7483 this.lastActive = false;
7487 * @event selectionchange
7488 * Fires when the selection changes
7489 * @param {SelectionModel} this
7491 "selectionchange" : true,
7493 * @event afterselectionchange
7494 * Fires after the selection changes (eg. by key press or clicking)
7495 * @param {SelectionModel} this
7497 "afterselectionchange" : true,
7499 * @event beforerowselect
7500 * Fires when a row is selected being selected, return false to cancel.
7501 * @param {SelectionModel} this
7502 * @param {Number} rowIndex The selected index
7503 * @param {Boolean} keepExisting False if other selections will be cleared
7505 "beforerowselect" : true,
7508 * Fires when a row is selected.
7509 * @param {SelectionModel} this
7510 * @param {Number} rowIndex The selected index
7511 * @param {Roo.data.Record} r The record
7515 * @event rowdeselect
7516 * Fires when a row is deselected.
7517 * @param {SelectionModel} this
7518 * @param {Number} rowIndex The selected index
7520 "rowdeselect" : true
7522 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7523 this.locked = false;
7526 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7528 * @cfg {Boolean} singleSelect
7529 * True to allow selection of only one row at a time (defaults to false)
7531 singleSelect : false,
7534 initEvents : function(){
7536 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7537 this.grid.on("mousedown", this.handleMouseDown, this);
7538 }else{ // allow click to work like normal
7539 this.grid.on("rowclick", this.handleDragableRowClick, this);
7541 // bootstrap does not have a view..
7542 var view = this.grid.view ? this.grid.view : this.grid;
7543 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7546 this.selectPrevious(e.shiftKey);
7547 }else if(this.last !== false && this.lastActive !== false){
7548 var last = this.last;
7549 this.selectRange(this.last, this.lastActive-1);
7550 view.focusRow(this.lastActive);
7555 this.selectFirstRow();
7557 this.fireEvent("afterselectionchange", this);
7559 "down" : function(e){
7561 this.selectNext(e.shiftKey);
7562 }else if(this.last !== false && this.lastActive !== false){
7563 var last = this.last;
7564 this.selectRange(this.last, this.lastActive+1);
7565 view.focusRow(this.lastActive);
7570 this.selectFirstRow();
7572 this.fireEvent("afterselectionchange", this);
7578 view.on("refresh", this.onRefresh, this);
7579 view.on("rowupdated", this.onRowUpdated, this);
7580 view.on("rowremoved", this.onRemove, this);
7584 onRefresh : function(){
7585 var ds = this.grid.ds, i, v = this.grid.view;
7586 var s = this.selections;
7588 if((i = ds.indexOfId(r.id)) != -1){
7590 s.add(ds.getAt(i)); // updating the selection relate data
7598 onRemove : function(v, index, r){
7599 this.selections.remove(r);
7603 onRowUpdated : function(v, index, r){
7604 if(this.isSelected(r)){
7605 v.onRowSelect(index);
7611 * @param {Array} records The records to select
7612 * @param {Boolean} keepExisting (optional) True to keep existing selections
7614 selectRecords : function(records, keepExisting){
7616 this.clearSelections();
7618 var ds = this.grid.ds;
7619 for(var i = 0, len = records.length; i < len; i++){
7620 this.selectRow(ds.indexOf(records[i]), true);
7625 * Gets the number of selected rows.
7628 getCount : function(){
7629 return this.selections.length;
7633 * Selects the first row in the grid.
7635 selectFirstRow : function(){
7640 * Select the last row.
7641 * @param {Boolean} keepExisting (optional) True to keep existing selections
7643 selectLastRow : function(keepExisting){
7644 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7648 * Selects the row immediately following the last selected row.
7649 * @param {Boolean} keepExisting (optional) True to keep existing selections
7651 selectNext : function(keepExisting){
7652 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7653 this.selectRow(this.last+1, keepExisting);
7654 var view = this.grid.view ? this.grid.view : this.grid;
7655 view.focusRow(this.last);
7660 * Selects the row that precedes the last selected row.
7661 * @param {Boolean} keepExisting (optional) True to keep existing selections
7663 selectPrevious : function(keepExisting){
7665 this.selectRow(this.last-1, keepExisting);
7666 var view = this.grid.view ? this.grid.view : this.grid;
7667 view.focusRow(this.last);
7672 * Returns the selected records
7673 * @return {Array} Array of selected records
7675 getSelections : function(){
7676 return [].concat(this.selections.items);
7680 * Returns the first selected record.
7683 getSelected : function(){
7684 return this.selections.itemAt(0);
7689 * Clears all selections.
7691 clearSelections : function(fast){
7696 var ds = this.grid.ds;
7697 var s = this.selections;
7699 this.deselectRow(ds.indexOfId(r.id));
7703 this.selections.clear();
7712 selectAll : function(){
7716 this.selections.clear();
7717 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7718 this.selectRow(i, true);
7723 * Returns True if there is a selection.
7726 hasSelection : function(){
7727 return this.selections.length > 0;
7731 * Returns True if the specified row is selected.
7732 * @param {Number/Record} record The record or index of the record to check
7735 isSelected : function(index){
7736 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7737 return (r && this.selections.key(r.id) ? true : false);
7741 * Returns True if the specified record id is selected.
7742 * @param {String} id The id of record to check
7745 isIdSelected : function(id){
7746 return (this.selections.key(id) ? true : false);
7750 handleMouseDown : function(e, t)
7752 var view = this.grid.view ? this.grid.view : this.grid;
7754 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7757 if(e.shiftKey && this.last !== false){
7758 var last = this.last;
7759 this.selectRange(last, rowIndex, e.ctrlKey);
7760 this.last = last; // reset the last
7761 view.focusRow(rowIndex);
7763 var isSelected = this.isSelected(rowIndex);
7764 if(e.button !== 0 && isSelected){
7765 view.focusRow(rowIndex);
7766 }else if(e.ctrlKey && isSelected){
7767 this.deselectRow(rowIndex);
7768 }else if(!isSelected){
7769 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7770 view.focusRow(rowIndex);
7773 this.fireEvent("afterselectionchange", this);
7776 handleDragableRowClick : function(grid, rowIndex, e)
7778 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7779 this.selectRow(rowIndex, false);
7780 var view = this.grid.view ? this.grid.view : this.grid;
7781 view.focusRow(rowIndex);
7782 this.fireEvent("afterselectionchange", this);
7787 * Selects multiple rows.
7788 * @param {Array} rows Array of the indexes of the row to select
7789 * @param {Boolean} keepExisting (optional) True to keep existing selections
7791 selectRows : function(rows, keepExisting){
7793 this.clearSelections();
7795 for(var i = 0, len = rows.length; i < len; i++){
7796 this.selectRow(rows[i], true);
7801 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7802 * @param {Number} startRow The index of the first row in the range
7803 * @param {Number} endRow The index of the last row in the range
7804 * @param {Boolean} keepExisting (optional) True to retain existing selections
7806 selectRange : function(startRow, endRow, keepExisting){
7811 this.clearSelections();
7813 if(startRow <= endRow){
7814 for(var i = startRow; i <= endRow; i++){
7815 this.selectRow(i, true);
7818 for(var i = startRow; i >= endRow; i--){
7819 this.selectRow(i, true);
7825 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7826 * @param {Number} startRow The index of the first row in the range
7827 * @param {Number} endRow The index of the last row in the range
7829 deselectRange : function(startRow, endRow, preventViewNotify){
7833 for(var i = startRow; i <= endRow; i++){
7834 this.deselectRow(i, preventViewNotify);
7840 * @param {Number} row The index of the row to select
7841 * @param {Boolean} keepExisting (optional) True to keep existing selections
7843 selectRow : function(index, keepExisting, preventViewNotify){
7844 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7847 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7848 if(!keepExisting || this.singleSelect){
7849 this.clearSelections();
7851 var r = this.grid.ds.getAt(index);
7852 this.selections.add(r);
7853 this.last = this.lastActive = index;
7854 if(!preventViewNotify){
7855 var view = this.grid.view ? this.grid.view : this.grid;
7856 view.onRowSelect(index);
7858 this.fireEvent("rowselect", this, index, r);
7859 this.fireEvent("selectionchange", this);
7865 * @param {Number} row The index of the row to deselect
7867 deselectRow : function(index, preventViewNotify){
7871 if(this.last == index){
7874 if(this.lastActive == index){
7875 this.lastActive = false;
7877 var r = this.grid.ds.getAt(index);
7878 this.selections.remove(r);
7879 if(!preventViewNotify){
7880 var view = this.grid.view ? this.grid.view : this.grid;
7881 view.onRowDeselect(index);
7883 this.fireEvent("rowdeselect", this, index);
7884 this.fireEvent("selectionchange", this);
7888 restoreLast : function(){
7890 this.last = this._last;
7895 acceptsNav : function(row, col, cm){
7896 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7900 onEditorKey : function(field, e){
7901 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7906 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7908 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7910 }else if(k == e.ENTER && !e.ctrlKey){
7914 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7916 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7918 }else if(k == e.ESC){
7922 g.startEditing(newCell[0], newCell[1]);
7927 * Ext JS Library 1.1.1
7928 * Copyright(c) 2006-2007, Ext JS, LLC.
7930 * Originally Released Under LGPL - original licence link has changed is not relivant.
7933 * <script type="text/javascript">
7938 * @class Roo.grid.ColumnModel
7939 * @extends Roo.util.Observable
7940 * This is the default implementation of a ColumnModel used by the Grid. It defines
7941 * the columns in the grid.
7944 var colModel = new Roo.grid.ColumnModel([
7945 {header: "Ticker", width: 60, sortable: true, locked: true},
7946 {header: "Company Name", width: 150, sortable: true},
7947 {header: "Market Cap.", width: 100, sortable: true},
7948 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7949 {header: "Employees", width: 100, sortable: true, resizable: false}
7954 * The config options listed for this class are options which may appear in each
7955 * individual column definition.
7956 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7958 * @param {Object} config An Array of column config objects. See this class's
7959 * config objects for details.
7961 Roo.grid.ColumnModel = function(config){
7963 * The config passed into the constructor
7965 this.config = []; //config;
7968 // if no id, create one
7969 // if the column does not have a dataIndex mapping,
7970 // map it to the order it is in the config
7971 for(var i = 0, len = config.length; i < len; i++){
7972 this.addColumn(config[i]);
7977 * The width of columns which have no width specified (defaults to 100)
7980 this.defaultWidth = 100;
7983 * Default sortable of columns which have no sortable specified (defaults to false)
7986 this.defaultSortable = false;
7990 * @event widthchange
7991 * Fires when the width of a column changes.
7992 * @param {ColumnModel} this
7993 * @param {Number} columnIndex The column index
7994 * @param {Number} newWidth The new width
7996 "widthchange": true,
7998 * @event headerchange
7999 * Fires when the text of a header changes.
8000 * @param {ColumnModel} this
8001 * @param {Number} columnIndex The column index
8002 * @param {Number} newText The new header text
8004 "headerchange": true,
8006 * @event hiddenchange
8007 * Fires when a column is hidden or "unhidden".
8008 * @param {ColumnModel} this
8009 * @param {Number} columnIndex The column index
8010 * @param {Boolean} hidden true if hidden, false otherwise
8012 "hiddenchange": true,
8014 * @event columnmoved
8015 * Fires when a column is moved.
8016 * @param {ColumnModel} this
8017 * @param {Number} oldIndex
8018 * @param {Number} newIndex
8020 "columnmoved" : true,
8022 * @event columlockchange
8023 * Fires when a column's locked state is changed
8024 * @param {ColumnModel} this
8025 * @param {Number} colIndex
8026 * @param {Boolean} locked true if locked
8028 "columnlockchange" : true
8030 Roo.grid.ColumnModel.superclass.constructor.call(this);
8032 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8034 * @cfg {String} header The header text to display in the Grid view.
8037 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8040 * @cfg {String} smHeader Header at Bootsrap Small width
8043 * @cfg {String} mdHeader Header at Bootsrap Medium width
8046 * @cfg {String} lgHeader Header at Bootsrap Large width
8049 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8052 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8053 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8054 * specified, the column's index is used as an index into the Record's data Array.
8057 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8058 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8061 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8062 * Defaults to the value of the {@link #defaultSortable} property.
8063 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8066 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8069 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8072 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8075 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8078 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8079 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8080 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8081 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8084 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8087 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8090 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8093 * @cfg {String} cursor (Optional)
8096 * @cfg {String} tooltip (Optional)
8099 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8102 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8105 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8108 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8111 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8114 * Returns the id of the column at the specified index.
8115 * @param {Number} index The column index
8116 * @return {String} the id
8118 getColumnId : function(index){
8119 return this.config[index].id;
8123 * Returns the column for a specified id.
8124 * @param {String} id The column id
8125 * @return {Object} the column
8127 getColumnById : function(id){
8128 return this.lookup[id];
8133 * Returns the column Object for a specified dataIndex.
8134 * @param {String} dataIndex The column dataIndex
8135 * @return {Object|Boolean} the column or false if not found
8137 getColumnByDataIndex: function(dataIndex){
8138 var index = this.findColumnIndex(dataIndex);
8139 return index > -1 ? this.config[index] : false;
8143 * Returns the index for a specified column id.
8144 * @param {String} id The column id
8145 * @return {Number} the index, or -1 if not found
8147 getIndexById : function(id){
8148 for(var i = 0, len = this.config.length; i < len; i++){
8149 if(this.config[i].id == id){
8157 * Returns the index for a specified column dataIndex.
8158 * @param {String} dataIndex The column dataIndex
8159 * @return {Number} the index, or -1 if not found
8162 findColumnIndex : function(dataIndex){
8163 for(var i = 0, len = this.config.length; i < len; i++){
8164 if(this.config[i].dataIndex == dataIndex){
8172 moveColumn : function(oldIndex, newIndex){
8173 var c = this.config[oldIndex];
8174 this.config.splice(oldIndex, 1);
8175 this.config.splice(newIndex, 0, c);
8176 this.dataMap = null;
8177 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8180 isLocked : function(colIndex){
8181 return this.config[colIndex].locked === true;
8184 setLocked : function(colIndex, value, suppressEvent){
8185 if(this.isLocked(colIndex) == value){
8188 this.config[colIndex].locked = value;
8190 this.fireEvent("columnlockchange", this, colIndex, value);
8194 getTotalLockedWidth : function(){
8196 for(var i = 0; i < this.config.length; i++){
8197 if(this.isLocked(i) && !this.isHidden(i)){
8198 this.totalWidth += this.getColumnWidth(i);
8204 getLockedCount : function(){
8205 for(var i = 0, len = this.config.length; i < len; i++){
8206 if(!this.isLocked(i)){
8211 return this.config.length;
8215 * Returns the number of columns.
8218 getColumnCount : function(visibleOnly){
8219 if(visibleOnly === true){
8221 for(var i = 0, len = this.config.length; i < len; i++){
8222 if(!this.isHidden(i)){
8228 return this.config.length;
8232 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8233 * @param {Function} fn
8234 * @param {Object} scope (optional)
8235 * @return {Array} result
8237 getColumnsBy : function(fn, scope){
8239 for(var i = 0, len = this.config.length; i < len; i++){
8240 var c = this.config[i];
8241 if(fn.call(scope||this, c, i) === true){
8249 * Returns true if the specified column is sortable.
8250 * @param {Number} col The column index
8253 isSortable : function(col){
8254 if(typeof this.config[col].sortable == "undefined"){
8255 return this.defaultSortable;
8257 return this.config[col].sortable;
8261 * Returns the rendering (formatting) function defined for the column.
8262 * @param {Number} col The column index.
8263 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8265 getRenderer : function(col){
8266 if(!this.config[col].renderer){
8267 return Roo.grid.ColumnModel.defaultRenderer;
8269 return this.config[col].renderer;
8273 * Sets the rendering (formatting) function for a column.
8274 * @param {Number} col The column index
8275 * @param {Function} fn The function to use to process the cell's raw data
8276 * to return HTML markup for the grid view. The render function is called with
8277 * the following parameters:<ul>
8278 * <li>Data value.</li>
8279 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8280 * <li>css A CSS style string to apply to the table cell.</li>
8281 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8282 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8283 * <li>Row index</li>
8284 * <li>Column index</li>
8285 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8287 setRenderer : function(col, fn){
8288 this.config[col].renderer = fn;
8292 * Returns the width for the specified column.
8293 * @param {Number} col The column index
8294 * @param (optional) {String} gridSize bootstrap width size.
8297 getColumnWidth : function(col, gridSize)
8299 var cfg = this.config[col];
8301 if (typeof(gridSize) == 'undefined') {
8302 return cfg.width * 1 || this.defaultWidth;
8304 if (gridSize === false) { // if we set it..
8305 return cfg.width || false;
8307 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8309 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8310 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8313 return cfg[ sizes[i] ];
8320 * Sets the width for a column.
8321 * @param {Number} col The column index
8322 * @param {Number} width The new width
8324 setColumnWidth : function(col, width, suppressEvent){
8325 this.config[col].width = width;
8326 this.totalWidth = null;
8328 this.fireEvent("widthchange", this, col, width);
8333 * Returns the total width of all columns.
8334 * @param {Boolean} includeHidden True to include hidden column widths
8337 getTotalWidth : function(includeHidden){
8338 if(!this.totalWidth){
8339 this.totalWidth = 0;
8340 for(var i = 0, len = this.config.length; i < len; i++){
8341 if(includeHidden || !this.isHidden(i)){
8342 this.totalWidth += this.getColumnWidth(i);
8346 return this.totalWidth;
8350 * Returns the header for the specified column.
8351 * @param {Number} col The column index
8354 getColumnHeader : function(col){
8355 return this.config[col].header;
8359 * Sets the header for a column.
8360 * @param {Number} col The column index
8361 * @param {String} header The new header
8363 setColumnHeader : function(col, header){
8364 this.config[col].header = header;
8365 this.fireEvent("headerchange", this, col, header);
8369 * Returns the tooltip for the specified column.
8370 * @param {Number} col The column index
8373 getColumnTooltip : function(col){
8374 return this.config[col].tooltip;
8377 * Sets the tooltip for a column.
8378 * @param {Number} col The column index
8379 * @param {String} tooltip The new tooltip
8381 setColumnTooltip : function(col, tooltip){
8382 this.config[col].tooltip = tooltip;
8386 * Returns the dataIndex for the specified column.
8387 * @param {Number} col The column index
8390 getDataIndex : function(col){
8391 return this.config[col].dataIndex;
8395 * Sets the dataIndex for a column.
8396 * @param {Number} col The column index
8397 * @param {Number} dataIndex The new dataIndex
8399 setDataIndex : function(col, dataIndex){
8400 this.config[col].dataIndex = dataIndex;
8406 * Returns true if the cell is editable.
8407 * @param {Number} colIndex The column index
8408 * @param {Number} rowIndex The row index - this is nto actually used..?
8411 isCellEditable : function(colIndex, rowIndex){
8412 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8416 * Returns the editor defined for the cell/column.
8417 * return false or null to disable editing.
8418 * @param {Number} colIndex The column index
8419 * @param {Number} rowIndex The row index
8422 getCellEditor : function(colIndex, rowIndex){
8423 return this.config[colIndex].editor;
8427 * Sets if a column is editable.
8428 * @param {Number} col The column index
8429 * @param {Boolean} editable True if the column is editable
8431 setEditable : function(col, editable){
8432 this.config[col].editable = editable;
8437 * Returns true if the column is hidden.
8438 * @param {Number} colIndex The column index
8441 isHidden : function(colIndex){
8442 return this.config[colIndex].hidden;
8447 * Returns true if the column width cannot be changed
8449 isFixed : function(colIndex){
8450 return this.config[colIndex].fixed;
8454 * Returns true if the column can be resized
8457 isResizable : function(colIndex){
8458 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8461 * Sets if a column is hidden.
8462 * @param {Number} colIndex The column index
8463 * @param {Boolean} hidden True if the column is hidden
8465 setHidden : function(colIndex, hidden){
8466 this.config[colIndex].hidden = hidden;
8467 this.totalWidth = null;
8468 this.fireEvent("hiddenchange", this, colIndex, hidden);
8472 * Sets the editor for a column.
8473 * @param {Number} col The column index
8474 * @param {Object} editor The editor object
8476 setEditor : function(col, editor){
8477 this.config[col].editor = editor;
8480 * Add a column (experimental...) - defaults to adding to the end..
8481 * @param {Object} config
8483 addColumn : function(c)
8486 var i = this.config.length;
8489 if(typeof c.dataIndex == "undefined"){
8492 if(typeof c.renderer == "string"){
8493 c.renderer = Roo.util.Format[c.renderer];
8495 if(typeof c.id == "undefined"){
8498 if(c.editor && c.editor.xtype){
8499 c.editor = Roo.factory(c.editor, Roo.grid);
8501 if(c.editor && c.editor.isFormField){
8502 c.editor = new Roo.grid.GridEditor(c.editor);
8504 this.lookup[c.id] = c;
8509 Roo.grid.ColumnModel.defaultRenderer = function(value)
8511 if(typeof value == "object") {
8514 if(typeof value == "string" && value.length < 1){
8518 return String.format("{0}", value);
8521 // Alias for backwards compatibility
8522 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8525 * Ext JS Library 1.1.1
8526 * Copyright(c) 2006-2007, Ext JS, LLC.
8528 * Originally Released Under LGPL - original licence link has changed is not relivant.
8531 * <script type="text/javascript">
8535 * @class Roo.LoadMask
8536 * A simple utility class for generically masking elements while loading data. If the element being masked has
8537 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8538 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8539 * element's UpdateManager load indicator and will be destroyed after the initial load.
8541 * Create a new LoadMask
8542 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8543 * @param {Object} config The config object
8545 Roo.LoadMask = function(el, config){
8546 this.el = Roo.get(el);
8547 Roo.apply(this, config);
8549 this.store.on('beforeload', this.onBeforeLoad, this);
8550 this.store.on('load', this.onLoad, this);
8551 this.store.on('loadexception', this.onLoadException, this);
8552 this.removeMask = false;
8554 var um = this.el.getUpdateManager();
8555 um.showLoadIndicator = false; // disable the default indicator
8556 um.on('beforeupdate', this.onBeforeLoad, this);
8557 um.on('update', this.onLoad, this);
8558 um.on('failure', this.onLoad, this);
8559 this.removeMask = true;
8563 Roo.LoadMask.prototype = {
8565 * @cfg {Boolean} removeMask
8566 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8567 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8571 * The text to display in a centered loading message box (defaults to 'Loading...')
8575 * @cfg {String} msgCls
8576 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8578 msgCls : 'x-mask-loading',
8581 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8587 * Disables the mask to prevent it from being displayed
8589 disable : function(){
8590 this.disabled = true;
8594 * Enables the mask so that it can be displayed
8596 enable : function(){
8597 this.disabled = false;
8600 onLoadException : function()
8604 if (typeof(arguments[3]) != 'undefined') {
8605 Roo.MessageBox.alert("Error loading",arguments[3]);
8609 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8610 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8617 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8622 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8626 onBeforeLoad : function(){
8628 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8633 destroy : function(){
8635 this.store.un('beforeload', this.onBeforeLoad, this);
8636 this.store.un('load', this.onLoad, this);
8637 this.store.un('loadexception', this.onLoadException, this);
8639 var um = this.el.getUpdateManager();
8640 um.un('beforeupdate', this.onBeforeLoad, this);
8641 um.un('update', this.onLoad, this);
8642 um.un('failure', this.onLoad, this);
8646 * @class Roo.bootstrap.Table
8648 * @extends Roo.bootstrap.Component
8649 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8650 * Similar to Roo.grid.Grid
8652 var table = Roo.factory({
8654 xns : Roo.bootstrap,
8655 autoSizeColumns: true,
8662 sortInfo : { direction : 'ASC', field: 'name' },
8664 xtype : 'HttpProxy',
8667 url : 'https://example.com/some.data.url.json'
8670 xtype : 'JsonReader',
8672 fields : [ 'id', 'name', whatever' ],
8679 xtype : 'ColumnModel',
8683 dataIndex : 'is_in_group',
8686 renderer : function(v, x , r) {
8688 return String.format("{0}", v)
8694 xtype : 'RowSelectionModel',
8695 xns : Roo.bootstrap.Table
8696 // you can add listeners to catch selection change here....
8702 grid.render(Roo.get("some-div"));
8705 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8710 * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8711 * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8712 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8714 * @cfg {String} cls table class
8717 * @cfg {boolean} striped Should the rows be alternative striped
8718 * @cfg {boolean} bordered Add borders to the table
8719 * @cfg {boolean} hover Add hover highlighting
8720 * @cfg {boolean} condensed Format condensed
8721 * @cfg {boolean} responsive Format condensed
8722 * @cfg {Boolean} loadMask (true|false) default false
8723 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8724 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8725 * @cfg {Boolean} rowSelection (true|false) default false
8726 * @cfg {Boolean} cellSelection (true|false) default false
8727 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8728 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8729 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8730 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8731 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8732 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8735 * Create a new Table
8736 * @param {Object} config The config object
8739 Roo.bootstrap.Table = function(config)
8741 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8744 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8745 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8746 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8747 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8749 this.view = this; // compat with grid.
8751 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8753 this.sm.grid = this;
8754 this.selModel = Roo.factory(this.sm, Roo.grid);
8755 this.sm = this.selModel;
8756 this.sm.xmodule = this.xmodule || false;
8759 if (this.cm && typeof(this.cm.config) == 'undefined') {
8760 this.colModel = new Roo.grid.ColumnModel(this.cm);
8761 this.cm = this.colModel;
8762 this.cm.xmodule = this.xmodule || false;
8765 this.store= Roo.factory(this.store, Roo.data);
8766 this.ds = this.store;
8767 this.ds.xmodule = this.xmodule || false;
8770 if (this.footer && this.store) {
8771 this.footer.dataSource = this.ds;
8772 this.footer = Roo.factory(this.footer);
8779 * Fires when a cell is clicked
8780 * @param {Roo.bootstrap.Table} this
8781 * @param {Roo.Element} el
8782 * @param {Number} rowIndex
8783 * @param {Number} columnIndex
8784 * @param {Roo.EventObject} e
8788 * @event celldblclick
8789 * Fires when a cell is double clicked
8790 * @param {Roo.bootstrap.Table} this
8791 * @param {Roo.Element} el
8792 * @param {Number} rowIndex
8793 * @param {Number} columnIndex
8794 * @param {Roo.EventObject} e
8796 "celldblclick" : true,
8799 * Fires when a row is clicked
8800 * @param {Roo.bootstrap.Table} this
8801 * @param {Roo.Element} el
8802 * @param {Number} rowIndex
8803 * @param {Roo.EventObject} e
8807 * @event rowdblclick
8808 * Fires when a row is double clicked
8809 * @param {Roo.bootstrap.Table} this
8810 * @param {Roo.Element} el
8811 * @param {Number} rowIndex
8812 * @param {Roo.EventObject} e
8814 "rowdblclick" : true,
8817 * Fires when a mouseover occur
8818 * @param {Roo.bootstrap.Table} this
8819 * @param {Roo.Element} el
8820 * @param {Number} rowIndex
8821 * @param {Number} columnIndex
8822 * @param {Roo.EventObject} e
8827 * Fires when a mouseout occur
8828 * @param {Roo.bootstrap.Table} this
8829 * @param {Roo.Element} el
8830 * @param {Number} rowIndex
8831 * @param {Number} columnIndex
8832 * @param {Roo.EventObject} e
8837 * Fires when a row is rendered, so you can change add a style to it.
8838 * @param {Roo.bootstrap.Table} this
8839 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8843 * @event rowsrendered
8844 * Fires when all the rows have been rendered
8845 * @param {Roo.bootstrap.Table} this
8847 'rowsrendered' : true,
8849 * @event contextmenu
8850 * The raw contextmenu event for the entire grid.
8851 * @param {Roo.EventObject} e
8853 "contextmenu" : true,
8855 * @event rowcontextmenu
8856 * Fires when a row is right clicked
8857 * @param {Roo.bootstrap.Table} this
8858 * @param {Number} rowIndex
8859 * @param {Roo.EventObject} e
8861 "rowcontextmenu" : true,
8863 * @event cellcontextmenu
8864 * Fires when a cell is right clicked
8865 * @param {Roo.bootstrap.Table} this
8866 * @param {Number} rowIndex
8867 * @param {Number} cellIndex
8868 * @param {Roo.EventObject} e
8870 "cellcontextmenu" : true,
8872 * @event headercontextmenu
8873 * Fires when a header is right clicked
8874 * @param {Roo.bootstrap.Table} this
8875 * @param {Number} columnIndex
8876 * @param {Roo.EventObject} e
8878 "headercontextmenu" : true,
8881 * The raw mousedown event for the entire grid.
8882 * @param {Roo.EventObject} e
8889 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8905 enableColumnResize: true,
8907 rowSelection : false,
8908 cellSelection : false,
8911 minColumnWidth : 50,
8913 // Roo.Element - the tbody
8914 bodyEl: false, // <tbody> Roo.Element - thead element
8915 headEl: false, // <thead> Roo.Element - thead element
8916 resizeProxy : false, // proxy element for dragging?
8920 container: false, // used by gridpanel...
8926 auto_hide_footer : false,
8928 view: false, // actually points to this..
8930 getAutoCreate : function()
8932 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8939 // this get's auto added by panel.Grid
8940 if (this.scrollBody) {
8941 cfg.cls += ' table-body-fixed';
8944 cfg.cls += ' table-striped';
8948 cfg.cls += ' table-hover';
8950 if (this.bordered) {
8951 cfg.cls += ' table-bordered';
8953 if (this.condensed) {
8954 cfg.cls += ' table-condensed';
8957 if (this.responsive) {
8958 cfg.cls += ' table-responsive';
8962 cfg.cls+= ' ' +this.cls;
8968 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8971 if(this.store || this.cm){
8972 if(this.headerShow){
8973 cfg.cn.push(this.renderHeader());
8976 cfg.cn.push(this.renderBody());
8978 if(this.footerShow){
8979 cfg.cn.push(this.renderFooter());
8981 // where does this come from?
8982 //cfg.cls+= ' TableGrid';
8985 return { cn : [ cfg ] };
8988 initEvents : function()
8990 if(!this.store || !this.cm){
8993 if (this.selModel) {
8994 this.selModel.initEvents();
8998 //Roo.log('initEvents with ds!!!!');
9000 this.bodyEl = this.el.select('tbody', true).first();
9001 this.headEl = this.el.select('thead', true).first();
9002 this.mainFoot = this.el.select('tfoot', true).first();
9007 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9008 e.on('click', this.sort, this);
9012 // why is this done????? = it breaks dialogs??
9013 //this.parent().el.setStyle('position', 'relative');
9017 this.footer.parentId = this.id;
9018 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9021 this.el.select('tfoot tr td').first().addClass('hide');
9026 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9029 this.store.on('load', this.onLoad, this);
9030 this.store.on('beforeload', this.onBeforeLoad, this);
9031 this.store.on('update', this.onUpdate, this);
9032 this.store.on('add', this.onAdd, this);
9033 this.store.on("clear", this.clear, this);
9035 this.el.on("contextmenu", this.onContextMenu, this);
9038 this.cm.on("headerchange", this.onHeaderChange, this);
9039 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9041 //?? does bodyEl get replaced on render?
9042 this.bodyEl.on("click", this.onClick, this);
9043 this.bodyEl.on("dblclick", this.onDblClick, this);
9044 this.bodyEl.on('scroll', this.onBodyScroll, this);
9046 // guessing mainbody will work - this relays usually caught by selmodel at present.
9047 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9050 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9053 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9054 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9059 // Compatibility with grid - we implement all the view features at present.
9060 getView : function()
9065 initCSS : function()
9069 var cm = this.cm, styles = [];
9070 this.CSS.removeStyleSheet(this.id + '-cssrules');
9071 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9072 // we can honour xs/sm/md/xl as widths...
9073 // we first have to decide what widht we are currently at...
9074 var sz = Roo.getGridSize();
9078 var cols = []; // visable cols.
9080 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9081 var w = cm.getColumnWidth(i, false);
9083 cols.push( { rel : false, abs : 0 });
9087 cols.push( { rel : false, abs : w });
9089 last = i; // not really..
9092 var w = cm.getColumnWidth(i, sz);
9097 cols.push( { rel : w, abs : false });
9100 var avail = this.bodyEl.dom.clientWidth - total_abs;
9102 var unitWidth = Math.floor(avail / total);
9103 var rem = avail - (unitWidth * total);
9105 var hidden, width, pos = 0 , splithide , left;
9106 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9108 hidden = 'display:none;';
9110 width = 'width:0px;';
9112 if(!cm.isHidden(i)){
9116 // we can honour xs/sm/md/xl ?
9117 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9119 hidden = 'display:none;';
9121 // width should return a small number...
9123 w+=rem; // add the remaining with..
9126 left = "left:" + (pos -4) + "px;";
9127 width = "width:" + w+ "px;";
9131 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9134 splithide = 'display:none;';
9137 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9138 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9143 //Roo.log(styles.join(''));
9144 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9150 onContextMenu : function(e, t)
9152 this.processEvent("contextmenu", e);
9155 processEvent : function(name, e)
9157 if (name != 'touchstart' ) {
9158 this.fireEvent(name, e);
9161 var t = e.getTarget();
9163 var cell = Roo.get(t);
9169 if(cell.findParent('tfoot', false, true)){
9173 if(cell.findParent('thead', false, true)){
9175 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9176 cell = Roo.get(t).findParent('th', false, true);
9178 Roo.log("failed to find th in thead?");
9179 Roo.log(e.getTarget());
9184 var cellIndex = cell.dom.cellIndex;
9186 var ename = name == 'touchstart' ? 'click' : name;
9187 this.fireEvent("header" + ename, this, cellIndex, e);
9192 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9193 cell = Roo.get(t).findParent('td', false, true);
9195 Roo.log("failed to find th in tbody?");
9196 Roo.log(e.getTarget());
9201 var row = cell.findParent('tr', false, true);
9202 var cellIndex = cell.dom.cellIndex;
9203 var rowIndex = row.dom.rowIndex - 1;
9207 this.fireEvent("row" + name, this, rowIndex, e);
9211 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9217 onMouseover : function(e, el)
9219 var cell = Roo.get(el);
9225 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9226 cell = cell.findParent('td', false, true);
9229 var row = cell.findParent('tr', false, true);
9230 var cellIndex = cell.dom.cellIndex;
9231 var rowIndex = row.dom.rowIndex - 1; // start from 0
9233 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9237 onMouseout : function(e, el)
9239 var cell = Roo.get(el);
9245 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9246 cell = cell.findParent('td', false, true);
9249 var row = cell.findParent('tr', false, true);
9250 var cellIndex = cell.dom.cellIndex;
9251 var rowIndex = row.dom.rowIndex - 1; // start from 0
9253 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9257 onClick : function(e, el)
9259 var cell = Roo.get(el);
9261 if(!cell || (!this.cellSelection && !this.rowSelection)){
9265 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9266 cell = cell.findParent('td', false, true);
9269 if(!cell || typeof(cell) == 'undefined'){
9273 var row = cell.findParent('tr', false, true);
9275 if(!row || typeof(row) == 'undefined'){
9279 var cellIndex = cell.dom.cellIndex;
9280 var rowIndex = this.getRowIndex(row);
9282 // why??? - should these not be based on SelectionModel?
9283 //if(this.cellSelection){
9284 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9287 //if(this.rowSelection){
9288 this.fireEvent('rowclick', this, row, rowIndex, e);
9293 onDblClick : function(e,el)
9295 var cell = Roo.get(el);
9297 if(!cell || (!this.cellSelection && !this.rowSelection)){
9301 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9302 cell = cell.findParent('td', false, true);
9305 if(!cell || typeof(cell) == 'undefined'){
9309 var row = cell.findParent('tr', false, true);
9311 if(!row || typeof(row) == 'undefined'){
9315 var cellIndex = cell.dom.cellIndex;
9316 var rowIndex = this.getRowIndex(row);
9318 if(this.cellSelection){
9319 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9322 if(this.rowSelection){
9323 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9326 findRowIndex : function(el)
9328 var cell = Roo.get(el);
9332 var row = cell.findParent('tr', false, true);
9334 if(!row || typeof(row) == 'undefined'){
9337 return this.getRowIndex(row);
9339 sort : function(e,el)
9341 var col = Roo.get(el);
9343 if(!col.hasClass('sortable')){
9347 var sort = col.attr('sort');
9350 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9354 this.store.sortInfo = {field : sort, direction : dir};
9357 Roo.log("calling footer first");
9358 this.footer.onClick('first');
9361 this.store.load({ params : { start : 0 } });
9365 renderHeader : function()
9373 this.totalWidth = 0;
9375 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9377 var config = cm.config[i];
9381 cls : 'x-hcol-' + i,
9384 html: cm.getColumnHeader(i)
9387 var tooltip = cm.getColumnTooltip(i);
9389 c.tooltip = tooltip;
9395 if(typeof(config.sortable) != 'undefined' && config.sortable){
9396 c.cls += ' sortable';
9397 c.html = '<i class="fa"></i>' + c.html;
9400 // could use BS4 hidden-..-down
9402 if(typeof(config.lgHeader) != 'undefined'){
9403 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9406 if(typeof(config.mdHeader) != 'undefined'){
9407 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9410 if(typeof(config.smHeader) != 'undefined'){
9411 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9414 if(typeof(config.xsHeader) != 'undefined'){
9415 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9422 if(typeof(config.tooltip) != 'undefined'){
9423 c.tooltip = config.tooltip;
9426 if(typeof(config.colspan) != 'undefined'){
9427 c.colspan = config.colspan;
9430 // hidden is handled by CSS now
9432 if(typeof(config.dataIndex) != 'undefined'){
9433 c.sort = config.dataIndex;
9438 if(typeof(config.align) != 'undefined' && config.align.length){
9439 c.style += ' text-align:' + config.align + ';';
9442 /* width is done in CSS
9443 *if(typeof(config.width) != 'undefined'){
9444 c.style += ' width:' + config.width + 'px;';
9445 this.totalWidth += config.width;
9447 this.totalWidth += 100; // assume minimum of 100 per column?
9451 if(typeof(config.cls) != 'undefined'){
9452 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9454 // this is the bit that doesnt reall work at all...
9456 if (this.responsive) {
9459 ['xs','sm','md','lg'].map(function(size){
9461 if(typeof(config[size]) == 'undefined'){
9465 if (!config[size]) { // 0 = hidden
9466 // BS 4 '0' is treated as hide that column and below.
9467 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9471 c.cls += ' col-' + size + '-' + config[size] + (
9472 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9480 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9491 renderBody : function()
9501 colspan : this.cm.getColumnCount()
9511 renderFooter : function()
9521 colspan : this.cm.getColumnCount()
9535 // Roo.log('ds onload');
9540 var ds = this.store;
9542 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9543 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9544 if (_this.store.sortInfo) {
9546 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9547 e.select('i', true).addClass(['fa-arrow-up']);
9550 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9551 e.select('i', true).addClass(['fa-arrow-down']);
9556 var tbody = this.bodyEl;
9558 if(ds.getCount() > 0){
9559 ds.data.each(function(d,rowIndex){
9560 var row = this.renderRow(cm, ds, rowIndex);
9562 tbody.createChild(row);
9566 if(row.cellObjects.length){
9567 Roo.each(row.cellObjects, function(r){
9568 _this.renderCellObject(r);
9575 var tfoot = this.el.select('tfoot', true).first();
9577 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9579 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9581 var total = this.ds.getTotalCount();
9583 if(this.footer.pageSize < total){
9584 this.mainFoot.show();
9588 Roo.each(this.el.select('tbody td', true).elements, function(e){
9589 e.on('mouseover', _this.onMouseover, _this);
9592 Roo.each(this.el.select('tbody td', true).elements, function(e){
9593 e.on('mouseout', _this.onMouseout, _this);
9595 this.fireEvent('rowsrendered', this);
9599 this.initCSS(); /// resize cols
9605 onUpdate : function(ds,record)
9607 this.refreshRow(record);
9611 onRemove : function(ds, record, index, isUpdate){
9612 if(isUpdate !== true){
9613 this.fireEvent("beforerowremoved", this, index, record);
9615 var bt = this.bodyEl.dom;
9617 var rows = this.el.select('tbody > tr', true).elements;
9619 if(typeof(rows[index]) != 'undefined'){
9620 bt.removeChild(rows[index].dom);
9623 // if(bt.rows[index]){
9624 // bt.removeChild(bt.rows[index]);
9627 if(isUpdate !== true){
9628 //this.stripeRows(index);
9629 //this.syncRowHeights(index, index);
9631 this.fireEvent("rowremoved", this, index, record);
9635 onAdd : function(ds, records, rowIndex)
9637 //Roo.log('on Add called');
9638 // - note this does not handle multiple adding very well..
9639 var bt = this.bodyEl.dom;
9640 for (var i =0 ; i < records.length;i++) {
9641 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9642 //Roo.log(records[i]);
9643 //Roo.log(this.store.getAt(rowIndex+i));
9644 this.insertRow(this.store, rowIndex + i, false);
9651 refreshRow : function(record){
9652 var ds = this.store, index;
9653 if(typeof record == 'number'){
9655 record = ds.getAt(index);
9657 index = ds.indexOf(record);
9659 return; // should not happen - but seems to
9662 this.insertRow(ds, index, true);
9664 this.onRemove(ds, record, index+1, true);
9666 //this.syncRowHeights(index, index);
9668 this.fireEvent("rowupdated", this, index, record);
9670 // private - called by RowSelection
9671 onRowSelect : function(rowIndex){
9672 var row = this.getRowDom(rowIndex);
9673 row.addClass(['bg-info','info']);
9675 // private - called by RowSelection
9676 onRowDeselect : function(rowIndex)
9681 var row = this.getRowDom(rowIndex);
9682 row.removeClass(['bg-info','info']);
9685 * Focuses the specified row.
9686 * @param {Number} row The row index
9688 focusRow : function(row)
9690 //Roo.log('GridView.focusRow');
9691 var x = this.bodyEl.dom.scrollLeft;
9692 this.focusCell(row, 0, false);
9693 this.bodyEl.dom.scrollLeft = x;
9697 * Focuses the specified cell.
9698 * @param {Number} row The row index
9699 * @param {Number} col The column index
9700 * @param {Boolean} hscroll false to disable horizontal scrolling
9702 focusCell : function(row, col, hscroll)
9704 //Roo.log('GridView.focusCell');
9705 var el = this.ensureVisible(row, col, hscroll);
9706 // not sure what focusEL achives = it's a <a> pos relative
9707 //this.focusEl.alignTo(el, "tl-tl");
9709 // this.focusEl.focus();
9711 // this.focusEl.focus.defer(1, this.focusEl);
9716 * Scrolls the specified cell into view
9717 * @param {Number} row The row index
9718 * @param {Number} col The column index
9719 * @param {Boolean} hscroll false to disable horizontal scrolling
9721 ensureVisible : function(row, col, hscroll)
9723 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9724 //return null; //disable for testing.
9725 if(typeof row != "number"){
9728 if(row < 0 && row >= this.ds.getCount()){
9731 col = (col !== undefined ? col : 0);
9733 while(cm.isHidden(col)){
9737 var el = this.getCellDom(row, col);
9741 var c = this.bodyEl.dom;
9743 var ctop = parseInt(el.offsetTop, 10);
9744 var cleft = parseInt(el.offsetLeft, 10);
9745 var cbot = ctop + el.offsetHeight;
9746 var cright = cleft + el.offsetWidth;
9748 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9749 var ch = 0; //?? header is not withing the area?
9750 var stop = parseInt(c.scrollTop, 10);
9751 var sleft = parseInt(c.scrollLeft, 10);
9752 var sbot = stop + ch;
9753 var sright = sleft + c.clientWidth;
9755 Roo.log('GridView.ensureVisible:' +
9757 ' c.clientHeight:' + c.clientHeight +
9758 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9767 //Roo.log("set scrolltop to ctop DISABLE?");
9768 }else if(cbot > sbot){
9769 //Roo.log("set scrolltop to cbot-ch");
9770 c.scrollTop = cbot-ch;
9773 if(hscroll !== false){
9775 c.scrollLeft = cleft;
9776 }else if(cright > sright){
9777 c.scrollLeft = cright-c.clientWidth;
9785 insertRow : function(dm, rowIndex, isUpdate){
9788 this.fireEvent("beforerowsinserted", this, rowIndex);
9790 //var s = this.getScrollState();
9791 var row = this.renderRow(this.cm, this.store, rowIndex);
9792 // insert before rowIndex..
9793 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9797 if(row.cellObjects.length){
9798 Roo.each(row.cellObjects, function(r){
9799 _this.renderCellObject(r);
9804 this.fireEvent("rowsinserted", this, rowIndex);
9805 //this.syncRowHeights(firstRow, lastRow);
9806 //this.stripeRows(firstRow);
9813 getRowDom : function(rowIndex)
9815 var rows = this.el.select('tbody > tr', true).elements;
9817 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9820 getCellDom : function(rowIndex, colIndex)
9822 var row = this.getRowDom(rowIndex);
9823 if (row === false) {
9826 var cols = row.select('td', true).elements;
9827 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9831 // returns the object tree for a tr..
9834 renderRow : function(cm, ds, rowIndex)
9836 var d = ds.getAt(rowIndex);
9840 cls : 'x-row-' + rowIndex,
9844 var cellObjects = [];
9846 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9847 var config = cm.config[i];
9849 var renderer = cm.getRenderer(i);
9853 if(typeof(renderer) !== 'undefined'){
9854 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9856 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9857 // and are rendered into the cells after the row is rendered - using the id for the element.
9859 if(typeof(value) === 'object'){
9869 rowIndex : rowIndex,
9874 this.fireEvent('rowclass', this, rowcfg);
9878 // this might end up displaying HTML?
9879 // this is too messy... - better to only do it on columsn you know are going to be too long
9880 //tooltip : (typeof(value) === 'object') ? '' : value,
9881 cls : rowcfg.rowClass + ' x-col-' + i,
9883 html: (typeof(value) === 'object') ? '' : value
9890 if(typeof(config.colspan) != 'undefined'){
9891 td.colspan = config.colspan;
9896 if(typeof(config.align) != 'undefined' && config.align.length){
9897 td.style += ' text-align:' + config.align + ';';
9899 if(typeof(config.valign) != 'undefined' && config.valign.length){
9900 td.style += ' vertical-align:' + config.valign + ';';
9903 if(typeof(config.width) != 'undefined'){
9904 td.style += ' width:' + config.width + 'px;';
9908 if(typeof(config.cursor) != 'undefined'){
9909 td.style += ' cursor:' + config.cursor + ';';
9912 if(typeof(config.cls) != 'undefined'){
9913 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9916 ['xs','sm','md','lg'].map(function(size){
9918 if(typeof(config[size]) == 'undefined'){
9924 if (!config[size]) { // 0 = hidden
9925 // BS 4 '0' is treated as hide that column and below.
9926 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9930 td.cls += ' col-' + size + '-' + config[size] + (
9931 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9941 row.cellObjects = cellObjects;
9949 onBeforeLoad : function()
9958 this.el.select('tbody', true).first().dom.innerHTML = '';
9961 * Show or hide a row.
9962 * @param {Number} rowIndex to show or hide
9963 * @param {Boolean} state hide
9965 setRowVisibility : function(rowIndex, state)
9967 var bt = this.bodyEl.dom;
9969 var rows = this.el.select('tbody > tr', true).elements;
9971 if(typeof(rows[rowIndex]) == 'undefined'){
9974 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9979 getSelectionModel : function(){
9981 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9983 return this.selModel;
9986 * Render the Roo.bootstrap object from renderder
9988 renderCellObject : function(r)
9992 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9994 var t = r.cfg.render(r.container);
9997 Roo.each(r.cfg.cn, function(c){
9999 container: t.getChildContainer(),
10002 _this.renderCellObject(child);
10007 * get the Row Index from a dom element.
10008 * @param {Roo.Element} row The row to look for
10009 * @returns {Number} the row
10011 getRowIndex : function(row)
10015 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10026 * get the header TH element for columnIndex
10027 * @param {Number} columnIndex
10028 * @returns {Roo.Element}
10030 getHeaderIndex: function(colIndex)
10032 var cols = this.headEl.select('th', true).elements;
10033 return cols[colIndex];
10036 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10037 * @param {domElement} cell to look for
10038 * @returns {Number} the column
10040 getCellIndex : function(cell)
10042 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10044 return parseInt(id[1], 10);
10049 * Returns the grid's underlying element = used by panel.Grid
10050 * @return {Element} The element
10052 getGridEl : function(){
10056 * Forces a resize - used by panel.Grid
10057 * @return {Element} The element
10059 autoSize : function()
10061 //var ctr = Roo.get(this.container.dom.parentElement);
10062 var ctr = Roo.get(this.el.dom);
10064 var thd = this.getGridEl().select('thead',true).first();
10065 var tbd = this.getGridEl().select('tbody', true).first();
10066 var tfd = this.getGridEl().select('tfoot', true).first();
10068 var cw = ctr.getWidth();
10069 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10073 tbd.setWidth(ctr.getWidth());
10074 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10075 // this needs fixing for various usage - currently only hydra job advers I think..
10077 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10079 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10082 cw = Math.max(cw, this.totalWidth);
10083 this.getGridEl().select('tbody tr',true).setWidth(cw);
10086 // resize 'expandable coloumn?
10088 return; // we doe not have a view in this design..
10091 onBodyScroll: function()
10093 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10095 this.headEl.setStyle({
10096 'position' : 'relative',
10097 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10103 var scrollHeight = this.bodyEl.dom.scrollHeight;
10105 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10107 var height = this.bodyEl.getHeight();
10109 if(scrollHeight - height == scrollTop) {
10111 var total = this.ds.getTotalCount();
10113 if(this.footer.cursor + this.footer.pageSize < total){
10115 this.footer.ds.load({
10117 start : this.footer.cursor + this.footer.pageSize,
10118 limit : this.footer.pageSize
10127 onColumnSplitterMoved : function(i, diff)
10129 this.userResized = true;
10131 var cm = this.colModel;
10133 var w = this.getHeaderIndex(i).getWidth() + diff;
10136 cm.setColumnWidth(i, w, true);
10138 //var cid = cm.getColumnId(i); << not used in this version?
10139 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10141 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10142 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10143 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10145 //this.updateSplitters();
10146 //this.layout(); << ??
10147 this.fireEvent("columnresize", i, w);
10149 onHeaderChange : function()
10151 var header = this.renderHeader();
10152 var table = this.el.select('table', true).first();
10154 this.headEl.remove();
10155 this.headEl = table.createChild(header, this.bodyEl, false);
10157 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10158 e.on('click', this.sort, this);
10161 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10162 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10167 onHiddenChange : function(colModel, colIndex, hidden)
10169 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10170 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10172 this.CSS.updateRule(thSelector, "display", "");
10173 this.CSS.updateRule(tdSelector, "display", "");
10176 this.CSS.updateRule(thSelector, "display", "none");
10177 this.CSS.updateRule(tdSelector, "display", "none");
10180 this.onHeaderChange();
10184 setColumnWidth: function(col_index, width)
10186 // width = "md-2 xs-2..."
10187 if(!this.colModel.config[col_index]) {
10191 var w = width.split(" ");
10193 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10195 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10198 for(var j = 0; j < w.length; j++) {
10204 var size_cls = w[j].split("-");
10206 if(!Number.isInteger(size_cls[1] * 1)) {
10210 if(!this.colModel.config[col_index][size_cls[0]]) {
10214 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10218 h_row[0].classList.replace(
10219 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10220 "col-"+size_cls[0]+"-"+size_cls[1]
10223 for(var i = 0; i < rows.length; i++) {
10225 var size_cls = w[j].split("-");
10227 if(!Number.isInteger(size_cls[1] * 1)) {
10231 if(!this.colModel.config[col_index][size_cls[0]]) {
10235 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10239 rows[i].classList.replace(
10240 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10241 "col-"+size_cls[0]+"-"+size_cls[1]
10245 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10250 // currently only used to find the split on drag..
10251 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10256 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10257 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10266 * @class Roo.bootstrap.TableCell
10267 * @extends Roo.bootstrap.Component
10268 * Bootstrap TableCell class
10269 * @cfg {String} html cell contain text
10270 * @cfg {String} cls cell class
10271 * @cfg {String} tag cell tag (td|th) default td
10272 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10273 * @cfg {String} align Aligns the content in a cell
10274 * @cfg {String} axis Categorizes cells
10275 * @cfg {String} bgcolor Specifies the background color of a cell
10276 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10277 * @cfg {Number} colspan Specifies the number of columns a cell should span
10278 * @cfg {String} headers Specifies one or more header cells a cell is related to
10279 * @cfg {Number} height Sets the height of a cell
10280 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10281 * @cfg {Number} rowspan Sets the number of rows a cell should span
10282 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10283 * @cfg {String} valign Vertical aligns the content in a cell
10284 * @cfg {Number} width Specifies the width of a cell
10287 * Create a new TableCell
10288 * @param {Object} config The config object
10291 Roo.bootstrap.TableCell = function(config){
10292 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10295 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10315 getAutoCreate : function(){
10316 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10323 cfg.tag = this.tag;
10336 cfg.align=this.align
10341 if (this.bgcolor) {
10342 cfg.bgcolor=this.bgcolor
10344 if (this.charoff) {
10345 cfg.charoff=this.charoff
10347 if (this.colspan) {
10348 cfg.colspan=this.colspan
10350 if (this.headers) {
10351 cfg.headers=this.headers
10354 cfg.height=this.height
10357 cfg.nowrap=this.nowrap
10359 if (this.rowspan) {
10360 cfg.rowspan=this.rowspan
10363 cfg.scope=this.scope
10366 cfg.valign=this.valign
10369 cfg.width=this.width
10388 * @class Roo.bootstrap.TableRow
10389 * @extends Roo.bootstrap.Component
10390 * Bootstrap TableRow class
10391 * @cfg {String} cls row class
10392 * @cfg {String} align Aligns the content in a table row
10393 * @cfg {String} bgcolor Specifies a background color for a table row
10394 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10395 * @cfg {String} valign Vertical aligns the content in a table row
10398 * Create a new TableRow
10399 * @param {Object} config The config object
10402 Roo.bootstrap.TableRow = function(config){
10403 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10406 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10414 getAutoCreate : function(){
10415 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10422 cfg.cls = this.cls;
10425 cfg.align = this.align;
10428 cfg.bgcolor = this.bgcolor;
10431 cfg.charoff = this.charoff;
10434 cfg.valign = this.valign;
10452 * @class Roo.bootstrap.TableBody
10453 * @extends Roo.bootstrap.Component
10454 * Bootstrap TableBody class
10455 * @cfg {String} cls element class
10456 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10457 * @cfg {String} align Aligns the content inside the element
10458 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10459 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10462 * Create a new TableBody
10463 * @param {Object} config The config object
10466 Roo.bootstrap.TableBody = function(config){
10467 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10470 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10478 getAutoCreate : function(){
10479 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10489 cfg.tag = this.tag;
10493 cfg.align = this.align;
10496 cfg.charoff = this.charoff;
10499 cfg.valign = this.valign;
10506 // initEvents : function()
10509 // if(!this.store){
10513 // this.store = Roo.factory(this.store, Roo.data);
10514 // this.store.on('load', this.onLoad, this);
10516 // this.store.load();
10520 // onLoad: function ()
10522 // this.fireEvent('load', this);
10532 * Ext JS Library 1.1.1
10533 * Copyright(c) 2006-2007, Ext JS, LLC.
10535 * Originally Released Under LGPL - original licence link has changed is not relivant.
10538 * <script type="text/javascript">
10541 // as we use this in bootstrap.
10542 Roo.namespace('Roo.form');
10544 * @class Roo.form.Action
10545 * Internal Class used to handle form actions
10547 * @param {Roo.form.BasicForm} el The form element or its id
10548 * @param {Object} config Configuration options
10553 // define the action interface
10554 Roo.form.Action = function(form, options){
10556 this.options = options || {};
10559 * Client Validation Failed
10562 Roo.form.Action.CLIENT_INVALID = 'client';
10564 * Server Validation Failed
10567 Roo.form.Action.SERVER_INVALID = 'server';
10569 * Connect to Server Failed
10572 Roo.form.Action.CONNECT_FAILURE = 'connect';
10574 * Reading Data from Server Failed
10577 Roo.form.Action.LOAD_FAILURE = 'load';
10579 Roo.form.Action.prototype = {
10581 failureType : undefined,
10582 response : undefined,
10583 result : undefined,
10585 // interface method
10586 run : function(options){
10590 // interface method
10591 success : function(response){
10595 // interface method
10596 handleResponse : function(response){
10600 // default connection failure
10601 failure : function(response){
10603 this.response = response;
10604 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10605 this.form.afterAction(this, false);
10608 processResponse : function(response){
10609 this.response = response;
10610 if(!response.responseText){
10613 this.result = this.handleResponse(response);
10614 return this.result;
10617 // utility functions used internally
10618 getUrl : function(appendParams){
10619 var url = this.options.url || this.form.url || this.form.el.dom.action;
10621 var p = this.getParams();
10623 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10629 getMethod : function(){
10630 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10633 getParams : function(){
10634 var bp = this.form.baseParams;
10635 var p = this.options.params;
10637 if(typeof p == "object"){
10638 p = Roo.urlEncode(Roo.applyIf(p, bp));
10639 }else if(typeof p == 'string' && bp){
10640 p += '&' + Roo.urlEncode(bp);
10643 p = Roo.urlEncode(bp);
10648 createCallback : function(){
10650 success: this.success,
10651 failure: this.failure,
10653 timeout: (this.form.timeout*1000),
10654 upload: this.form.fileUpload ? this.success : undefined
10659 Roo.form.Action.Submit = function(form, options){
10660 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10663 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10666 haveProgress : false,
10667 uploadComplete : false,
10669 // uploadProgress indicator.
10670 uploadProgress : function()
10672 if (!this.form.progressUrl) {
10676 if (!this.haveProgress) {
10677 Roo.MessageBox.progress("Uploading", "Uploading");
10679 if (this.uploadComplete) {
10680 Roo.MessageBox.hide();
10684 this.haveProgress = true;
10686 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10688 var c = new Roo.data.Connection();
10690 url : this.form.progressUrl,
10695 success : function(req){
10696 //console.log(data);
10700 rdata = Roo.decode(req.responseText)
10702 Roo.log("Invalid data from server..");
10706 if (!rdata || !rdata.success) {
10708 Roo.MessageBox.alert(Roo.encode(rdata));
10711 var data = rdata.data;
10713 if (this.uploadComplete) {
10714 Roo.MessageBox.hide();
10719 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10720 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10723 this.uploadProgress.defer(2000,this);
10726 failure: function(data) {
10727 Roo.log('progress url failed ');
10738 // run get Values on the form, so it syncs any secondary forms.
10739 this.form.getValues();
10741 var o = this.options;
10742 var method = this.getMethod();
10743 var isPost = method == 'POST';
10744 if(o.clientValidation === false || this.form.isValid()){
10746 if (this.form.progressUrl) {
10747 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10748 (new Date() * 1) + '' + Math.random());
10753 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10754 form:this.form.el.dom,
10755 url:this.getUrl(!isPost),
10757 params:isPost ? this.getParams() : null,
10758 isUpload: this.form.fileUpload,
10759 formData : this.form.formData
10762 this.uploadProgress();
10764 }else if (o.clientValidation !== false){ // client validation failed
10765 this.failureType = Roo.form.Action.CLIENT_INVALID;
10766 this.form.afterAction(this, false);
10770 success : function(response)
10772 this.uploadComplete= true;
10773 if (this.haveProgress) {
10774 Roo.MessageBox.hide();
10778 var result = this.processResponse(response);
10779 if(result === true || result.success){
10780 this.form.afterAction(this, true);
10784 this.form.markInvalid(result.errors);
10785 this.failureType = Roo.form.Action.SERVER_INVALID;
10787 this.form.afterAction(this, false);
10789 failure : function(response)
10791 this.uploadComplete= true;
10792 if (this.haveProgress) {
10793 Roo.MessageBox.hide();
10796 this.response = response;
10797 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10798 this.form.afterAction(this, false);
10801 handleResponse : function(response){
10802 if(this.form.errorReader){
10803 var rs = this.form.errorReader.read(response);
10806 for(var i = 0, len = rs.records.length; i < len; i++) {
10807 var r = rs.records[i];
10808 errors[i] = r.data;
10811 if(errors.length < 1){
10815 success : rs.success,
10821 ret = Roo.decode(response.responseText);
10825 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10835 Roo.form.Action.Load = function(form, options){
10836 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10837 this.reader = this.form.reader;
10840 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10845 Roo.Ajax.request(Roo.apply(
10846 this.createCallback(), {
10847 method:this.getMethod(),
10848 url:this.getUrl(false),
10849 params:this.getParams()
10853 success : function(response){
10855 var result = this.processResponse(response);
10856 if(result === true || !result.success || !result.data){
10857 this.failureType = Roo.form.Action.LOAD_FAILURE;
10858 this.form.afterAction(this, false);
10861 this.form.clearInvalid();
10862 this.form.setValues(result.data);
10863 this.form.afterAction(this, true);
10866 handleResponse : function(response){
10867 if(this.form.reader){
10868 var rs = this.form.reader.read(response);
10869 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10871 success : rs.success,
10875 return Roo.decode(response.responseText);
10879 Roo.form.Action.ACTION_TYPES = {
10880 'load' : Roo.form.Action.Load,
10881 'submit' : Roo.form.Action.Submit
10890 * @class Roo.bootstrap.Form
10891 * @extends Roo.bootstrap.Component
10892 * Bootstrap Form class
10893 * @cfg {String} method GET | POST (default POST)
10894 * @cfg {String} labelAlign top | left (default top)
10895 * @cfg {String} align left | right - for navbars
10896 * @cfg {Boolean} loadMask load mask when submit (default true)
10900 * Create a new Form
10901 * @param {Object} config The config object
10905 Roo.bootstrap.Form = function(config){
10907 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10909 Roo.bootstrap.Form.popover.apply();
10913 * @event clientvalidation
10914 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10915 * @param {Form} this
10916 * @param {Boolean} valid true if the form has passed client-side validation
10918 clientvalidation: true,
10920 * @event beforeaction
10921 * Fires before any action is performed. Return false to cancel the action.
10922 * @param {Form} this
10923 * @param {Action} action The action to be performed
10925 beforeaction: true,
10927 * @event actionfailed
10928 * Fires when an action fails.
10929 * @param {Form} this
10930 * @param {Action} action The action that failed
10932 actionfailed : true,
10934 * @event actioncomplete
10935 * Fires when an action is completed.
10936 * @param {Form} this
10937 * @param {Action} action The action that completed
10939 actioncomplete : true
10943 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10946 * @cfg {String} method
10947 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10951 * @cfg {String} url
10952 * The URL to use for form actions if one isn't supplied in the action options.
10955 * @cfg {Boolean} fileUpload
10956 * Set to true if this form is a file upload.
10960 * @cfg {Object} baseParams
10961 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10965 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10969 * @cfg {Sting} align (left|right) for navbar forms
10974 activeAction : null,
10977 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10978 * element by passing it or its id or mask the form itself by passing in true.
10981 waitMsgTarget : false,
10986 * @cfg {Boolean} errorMask (true|false) default false
10991 * @cfg {Number} maskOffset Default 100
10996 * @cfg {Boolean} maskBody
11000 getAutoCreate : function(){
11004 method : this.method || 'POST',
11005 id : this.id || Roo.id(),
11008 if (this.parent().xtype.match(/^Nav/)) {
11009 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11013 if (this.labelAlign == 'left' ) {
11014 cfg.cls += ' form-horizontal';
11020 initEvents : function()
11022 this.el.on('submit', this.onSubmit, this);
11023 // this was added as random key presses on the form where triggering form submit.
11024 this.el.on('keypress', function(e) {
11025 if (e.getCharCode() != 13) {
11028 // we might need to allow it for textareas.. and some other items.
11029 // check e.getTarget().
11031 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11035 Roo.log("keypress blocked");
11037 e.preventDefault();
11043 onSubmit : function(e){
11048 * Returns true if client-side validation on the form is successful.
11051 isValid : function(){
11052 var items = this.getItems();
11054 var target = false;
11056 items.each(function(f){
11062 Roo.log('invalid field: ' + f.name);
11066 if(!target && f.el.isVisible(true)){
11072 if(this.errorMask && !valid){
11073 Roo.bootstrap.Form.popover.mask(this, target);
11080 * Returns true if any fields in this form have changed since their original load.
11083 isDirty : function(){
11085 var items = this.getItems();
11086 items.each(function(f){
11096 * Performs a predefined action (submit or load) or custom actions you define on this form.
11097 * @param {String} actionName The name of the action type
11098 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11099 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11100 * accept other config options):
11102 Property Type Description
11103 ---------------- --------------- ----------------------------------------------------------------------------------
11104 url String The url for the action (defaults to the form's url)
11105 method String The form method to use (defaults to the form's method, or POST if not defined)
11106 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11107 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11108 validate the form on the client (defaults to false)
11110 * @return {BasicForm} this
11112 doAction : function(action, options){
11113 if(typeof action == 'string'){
11114 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11116 if(this.fireEvent('beforeaction', this, action) !== false){
11117 this.beforeAction(action);
11118 action.run.defer(100, action);
11124 beforeAction : function(action){
11125 var o = action.options;
11130 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11132 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11135 // not really supported yet.. ??
11137 //if(this.waitMsgTarget === true){
11138 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11139 //}else if(this.waitMsgTarget){
11140 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11141 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11143 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11149 afterAction : function(action, success){
11150 this.activeAction = null;
11151 var o = action.options;
11156 Roo.get(document.body).unmask();
11162 //if(this.waitMsgTarget === true){
11163 // this.el.unmask();
11164 //}else if(this.waitMsgTarget){
11165 // this.waitMsgTarget.unmask();
11167 // Roo.MessageBox.updateProgress(1);
11168 // Roo.MessageBox.hide();
11175 Roo.callback(o.success, o.scope, [this, action]);
11176 this.fireEvent('actioncomplete', this, action);
11180 // failure condition..
11181 // we have a scenario where updates need confirming.
11182 // eg. if a locking scenario exists..
11183 // we look for { errors : { needs_confirm : true }} in the response.
11185 (typeof(action.result) != 'undefined') &&
11186 (typeof(action.result.errors) != 'undefined') &&
11187 (typeof(action.result.errors.needs_confirm) != 'undefined')
11190 Roo.log("not supported yet");
11193 Roo.MessageBox.confirm(
11194 "Change requires confirmation",
11195 action.result.errorMsg,
11200 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11210 Roo.callback(o.failure, o.scope, [this, action]);
11211 // show an error message if no failed handler is set..
11212 if (!this.hasListener('actionfailed')) {
11213 Roo.log("need to add dialog support");
11215 Roo.MessageBox.alert("Error",
11216 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11217 action.result.errorMsg :
11218 "Saving Failed, please check your entries or try again"
11223 this.fireEvent('actionfailed', this, action);
11228 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11229 * @param {String} id The value to search for
11232 findField : function(id){
11233 var items = this.getItems();
11234 var field = items.get(id);
11236 items.each(function(f){
11237 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11244 return field || null;
11247 * Mark fields in this form invalid in bulk.
11248 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11249 * @return {BasicForm} this
11251 markInvalid : function(errors){
11252 if(errors instanceof Array){
11253 for(var i = 0, len = errors.length; i < len; i++){
11254 var fieldError = errors[i];
11255 var f = this.findField(fieldError.id);
11257 f.markInvalid(fieldError.msg);
11263 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11264 field.markInvalid(errors[id]);
11268 //Roo.each(this.childForms || [], function (f) {
11269 // f.markInvalid(errors);
11276 * Set values for fields in this form in bulk.
11277 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11278 * @return {BasicForm} this
11280 setValues : function(values){
11281 if(values instanceof Array){ // array of objects
11282 for(var i = 0, len = values.length; i < len; i++){
11284 var f = this.findField(v.id);
11286 f.setValue(v.value);
11287 if(this.trackResetOnLoad){
11288 f.originalValue = f.getValue();
11292 }else{ // object hash
11295 if(typeof values[id] != 'function' && (field = this.findField(id))){
11297 if (field.setFromData &&
11298 field.valueField &&
11299 field.displayField &&
11300 // combos' with local stores can
11301 // be queried via setValue()
11302 // to set their value..
11303 (field.store && !field.store.isLocal)
11307 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11308 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11309 field.setFromData(sd);
11311 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11313 field.setFromData(values);
11316 field.setValue(values[id]);
11320 if(this.trackResetOnLoad){
11321 field.originalValue = field.getValue();
11327 //Roo.each(this.childForms || [], function (f) {
11328 // f.setValues(values);
11335 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11336 * they are returned as an array.
11337 * @param {Boolean} asString
11340 getValues : function(asString){
11341 //if (this.childForms) {
11342 // copy values from the child forms
11343 // Roo.each(this.childForms, function (f) {
11344 // this.setValues(f.getValues());
11350 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11351 if(asString === true){
11354 return Roo.urlDecode(fs);
11358 * Returns the fields in this form as an object with key/value pairs.
11359 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11362 getFieldValues : function(with_hidden)
11364 var items = this.getItems();
11366 items.each(function(f){
11368 if (!f.getName()) {
11372 var v = f.getValue();
11374 if (f.inputType =='radio') {
11375 if (typeof(ret[f.getName()]) == 'undefined') {
11376 ret[f.getName()] = ''; // empty..
11379 if (!f.el.dom.checked) {
11383 v = f.el.dom.value;
11387 if(f.xtype == 'MoneyField'){
11388 ret[f.currencyName] = f.getCurrency();
11391 // not sure if this supported any more..
11392 if ((typeof(v) == 'object') && f.getRawValue) {
11393 v = f.getRawValue() ; // dates..
11395 // combo boxes where name != hiddenName...
11396 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11397 ret[f.name] = f.getRawValue();
11399 ret[f.getName()] = v;
11406 * Clears all invalid messages in this form.
11407 * @return {BasicForm} this
11409 clearInvalid : function(){
11410 var items = this.getItems();
11412 items.each(function(f){
11420 * Resets this form.
11421 * @return {BasicForm} this
11423 reset : function(){
11424 var items = this.getItems();
11425 items.each(function(f){
11429 Roo.each(this.childForms || [], function (f) {
11437 getItems : function()
11439 var r=new Roo.util.MixedCollection(false, function(o){
11440 return o.id || (o.id = Roo.id());
11442 var iter = function(el) {
11449 Roo.each(el.items,function(e) {
11458 hideFields : function(items)
11460 Roo.each(items, function(i){
11462 var f = this.findField(i);
11473 showFields : function(items)
11475 Roo.each(items, function(i){
11477 var f = this.findField(i);
11490 Roo.apply(Roo.bootstrap.Form, {
11506 intervalID : false,
11512 if(this.isApplied){
11517 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11518 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11519 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11520 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11523 this.maskEl.top.enableDisplayMode("block");
11524 this.maskEl.left.enableDisplayMode("block");
11525 this.maskEl.bottom.enableDisplayMode("block");
11526 this.maskEl.right.enableDisplayMode("block");
11528 this.toolTip = new Roo.bootstrap.Tooltip({
11529 cls : 'roo-form-error-popover',
11531 'left' : ['r-l', [-2,0], 'right'],
11532 'right' : ['l-r', [2,0], 'left'],
11533 'bottom' : ['tl-bl', [0,2], 'top'],
11534 'top' : [ 'bl-tl', [0,-2], 'bottom']
11538 this.toolTip.render(Roo.get(document.body));
11540 this.toolTip.el.enableDisplayMode("block");
11542 Roo.get(document.body).on('click', function(){
11546 Roo.get(document.body).on('touchstart', function(){
11550 this.isApplied = true
11553 mask : function(form, target)
11557 this.target = target;
11559 if(!this.form.errorMask || !target.el){
11563 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11565 Roo.log(scrollable);
11567 var ot = this.target.el.calcOffsetsTo(scrollable);
11569 var scrollTo = ot[1] - this.form.maskOffset;
11571 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11573 scrollable.scrollTo('top', scrollTo);
11575 var box = this.target.el.getBox();
11577 var zIndex = Roo.bootstrap.Modal.zIndex++;
11580 this.maskEl.top.setStyle('position', 'absolute');
11581 this.maskEl.top.setStyle('z-index', zIndex);
11582 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11583 this.maskEl.top.setLeft(0);
11584 this.maskEl.top.setTop(0);
11585 this.maskEl.top.show();
11587 this.maskEl.left.setStyle('position', 'absolute');
11588 this.maskEl.left.setStyle('z-index', zIndex);
11589 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11590 this.maskEl.left.setLeft(0);
11591 this.maskEl.left.setTop(box.y - this.padding);
11592 this.maskEl.left.show();
11594 this.maskEl.bottom.setStyle('position', 'absolute');
11595 this.maskEl.bottom.setStyle('z-index', zIndex);
11596 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11597 this.maskEl.bottom.setLeft(0);
11598 this.maskEl.bottom.setTop(box.bottom + this.padding);
11599 this.maskEl.bottom.show();
11601 this.maskEl.right.setStyle('position', 'absolute');
11602 this.maskEl.right.setStyle('z-index', zIndex);
11603 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11604 this.maskEl.right.setLeft(box.right + this.padding);
11605 this.maskEl.right.setTop(box.y - this.padding);
11606 this.maskEl.right.show();
11608 this.toolTip.bindEl = this.target.el;
11610 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11612 var tip = this.target.blankText;
11614 if(this.target.getValue() !== '' ) {
11616 if (this.target.invalidText.length) {
11617 tip = this.target.invalidText;
11618 } else if (this.target.regexText.length){
11619 tip = this.target.regexText;
11623 this.toolTip.show(tip);
11625 this.intervalID = window.setInterval(function() {
11626 Roo.bootstrap.Form.popover.unmask();
11629 window.onwheel = function(){ return false;};
11631 (function(){ this.isMasked = true; }).defer(500, this);
11635 unmask : function()
11637 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11641 this.maskEl.top.setStyle('position', 'absolute');
11642 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11643 this.maskEl.top.hide();
11645 this.maskEl.left.setStyle('position', 'absolute');
11646 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11647 this.maskEl.left.hide();
11649 this.maskEl.bottom.setStyle('position', 'absolute');
11650 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11651 this.maskEl.bottom.hide();
11653 this.maskEl.right.setStyle('position', 'absolute');
11654 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11655 this.maskEl.right.hide();
11657 this.toolTip.hide();
11659 this.toolTip.el.hide();
11661 window.onwheel = function(){ return true;};
11663 if(this.intervalID){
11664 window.clearInterval(this.intervalID);
11665 this.intervalID = false;
11668 this.isMasked = false;
11678 * Ext JS Library 1.1.1
11679 * Copyright(c) 2006-2007, Ext JS, LLC.
11681 * Originally Released Under LGPL - original licence link has changed is not relivant.
11684 * <script type="text/javascript">
11687 * @class Roo.form.VTypes
11688 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11691 Roo.form.VTypes = function(){
11692 // closure these in so they are only created once.
11693 var alpha = /^[a-zA-Z_]+$/;
11694 var alphanum = /^[a-zA-Z0-9_]+$/;
11695 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11696 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11698 // All these messages and functions are configurable
11701 * The function used to validate email addresses
11702 * @param {String} value The email address
11704 'email' : function(v){
11705 return email.test(v);
11708 * The error text to display when the email validation function returns false
11711 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11713 * The keystroke filter mask to be applied on email input
11716 'emailMask' : /[a-z0-9_\.\-@]/i,
11719 * The function used to validate URLs
11720 * @param {String} value The URL
11722 'url' : function(v){
11723 return url.test(v);
11726 * The error text to display when the url validation function returns false
11729 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11732 * The function used to validate alpha values
11733 * @param {String} value The value
11735 'alpha' : function(v){
11736 return alpha.test(v);
11739 * The error text to display when the alpha validation function returns false
11742 'alphaText' : 'This field should only contain letters and _',
11744 * The keystroke filter mask to be applied on alpha input
11747 'alphaMask' : /[a-z_]/i,
11750 * The function used to validate alphanumeric values
11751 * @param {String} value The value
11753 'alphanum' : function(v){
11754 return alphanum.test(v);
11757 * The error text to display when the alphanumeric validation function returns false
11760 'alphanumText' : 'This field should only contain letters, numbers and _',
11762 * The keystroke filter mask to be applied on alphanumeric input
11765 'alphanumMask' : /[a-z0-9_]/i
11775 * @class Roo.bootstrap.Input
11776 * @extends Roo.bootstrap.Component
11777 * Bootstrap Input class
11778 * @cfg {Boolean} disabled is it disabled
11779 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11780 * @cfg {String} name name of the input
11781 * @cfg {string} fieldLabel - the label associated
11782 * @cfg {string} placeholder - placeholder to put in text.
11783 * @cfg {string} before - input group add on before
11784 * @cfg {string} after - input group add on after
11785 * @cfg {string} size - (lg|sm) or leave empty..
11786 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11787 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11788 * @cfg {Number} md colspan out of 12 for computer-sized screens
11789 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11790 * @cfg {string} value default value of the input
11791 * @cfg {Number} labelWidth set the width of label
11792 * @cfg {Number} labellg set the width of label (1-12)
11793 * @cfg {Number} labelmd set the width of label (1-12)
11794 * @cfg {Number} labelsm set the width of label (1-12)
11795 * @cfg {Number} labelxs set the width of label (1-12)
11796 * @cfg {String} labelAlign (top|left)
11797 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11798 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11799 * @cfg {String} indicatorpos (left|right) default left
11800 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11801 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11802 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11804 * @cfg {String} align (left|center|right) Default left
11805 * @cfg {Boolean} forceFeedback (true|false) Default false
11808 * Create a new Input
11809 * @param {Object} config The config object
11812 Roo.bootstrap.Input = function(config){
11814 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11819 * Fires when this field receives input focus.
11820 * @param {Roo.form.Field} this
11825 * Fires when this field loses input focus.
11826 * @param {Roo.form.Field} this
11830 * @event specialkey
11831 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11832 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11833 * @param {Roo.form.Field} this
11834 * @param {Roo.EventObject} e The event object
11839 * Fires just before the field blurs if the field value has changed.
11840 * @param {Roo.form.Field} this
11841 * @param {Mixed} newValue The new value
11842 * @param {Mixed} oldValue The original value
11847 * Fires after the field has been marked as invalid.
11848 * @param {Roo.form.Field} this
11849 * @param {String} msg The validation message
11854 * Fires after the field has been validated with no errors.
11855 * @param {Roo.form.Field} this
11860 * Fires after the key up
11861 * @param {Roo.form.Field} this
11862 * @param {Roo.EventObject} e The event Object
11867 * Fires after the user pastes into input
11868 * @param {Roo.form.Field} this
11869 * @param {Roo.EventObject} e The event Object
11875 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11877 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11878 automatic validation (defaults to "keyup").
11880 validationEvent : "keyup",
11882 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11884 validateOnBlur : true,
11886 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11888 validationDelay : 250,
11890 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11892 focusClass : "x-form-focus", // not needed???
11896 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11898 invalidClass : "has-warning",
11901 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11903 validClass : "has-success",
11906 * @cfg {Boolean} hasFeedback (true|false) default true
11908 hasFeedback : true,
11911 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11913 invalidFeedbackClass : "glyphicon-warning-sign",
11916 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11918 validFeedbackClass : "glyphicon-ok",
11921 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11923 selectOnFocus : false,
11926 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11930 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11935 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11937 disableKeyFilter : false,
11940 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11944 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11948 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11950 blankText : "Please complete this mandatory field",
11953 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11957 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11959 maxLength : Number.MAX_VALUE,
11961 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11963 minLengthText : "The minimum length for this field is {0}",
11965 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11967 maxLengthText : "The maximum length for this field is {0}",
11971 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11972 * If available, this function will be called only after the basic validators all return true, and will be passed the
11973 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11977 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11978 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11979 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11983 * @cfg {String} regexText -- Depricated - use Invalid Text
11988 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11994 autocomplete: false,
11998 inputType : 'text',
12001 placeholder: false,
12006 preventMark: false,
12007 isFormField : true,
12010 labelAlign : false,
12013 formatedValue : false,
12014 forceFeedback : false,
12016 indicatorpos : 'left',
12026 parentLabelAlign : function()
12029 while (parent.parent()) {
12030 parent = parent.parent();
12031 if (typeof(parent.labelAlign) !='undefined') {
12032 return parent.labelAlign;
12039 getAutoCreate : function()
12041 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12047 if(this.inputType != 'hidden'){
12048 cfg.cls = 'form-group' //input-group
12054 type : this.inputType,
12055 value : this.value,
12056 cls : 'form-control',
12057 placeholder : this.placeholder || '',
12058 autocomplete : this.autocomplete || 'new-password'
12060 if (this.inputType == 'file') {
12061 input.style = 'overflow:hidden'; // why not in CSS?
12064 if(this.capture.length){
12065 input.capture = this.capture;
12068 if(this.accept.length){
12069 input.accept = this.accept + "/*";
12073 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12076 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12077 input.maxLength = this.maxLength;
12080 if (this.disabled) {
12081 input.disabled=true;
12084 if (this.readOnly) {
12085 input.readonly=true;
12089 input.name = this.name;
12093 input.cls += ' input-' + this.size;
12097 ['xs','sm','md','lg'].map(function(size){
12098 if (settings[size]) {
12099 cfg.cls += ' col-' + size + '-' + settings[size];
12103 var inputblock = input;
12107 cls: 'glyphicon form-control-feedback'
12110 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12113 cls : 'has-feedback',
12121 if (this.before || this.after) {
12124 cls : 'input-group',
12128 if (this.before && typeof(this.before) == 'string') {
12130 inputblock.cn.push({
12132 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12136 if (this.before && typeof(this.before) == 'object') {
12137 this.before = Roo.factory(this.before);
12139 inputblock.cn.push({
12141 cls : 'roo-input-before input-group-prepend input-group-' +
12142 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12146 inputblock.cn.push(input);
12148 if (this.after && typeof(this.after) == 'string') {
12149 inputblock.cn.push({
12151 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12155 if (this.after && typeof(this.after) == 'object') {
12156 this.after = Roo.factory(this.after);
12158 inputblock.cn.push({
12160 cls : 'roo-input-after input-group-append input-group-' +
12161 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12165 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12166 inputblock.cls += ' has-feedback';
12167 inputblock.cn.push(feedback);
12172 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12173 tooltip : 'This field is required'
12175 if (this.allowBlank ) {
12176 indicator.style = this.allowBlank ? ' display:none' : '';
12178 if (align ==='left' && this.fieldLabel.length) {
12180 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12187 cls : 'control-label col-form-label',
12188 html : this.fieldLabel
12199 var labelCfg = cfg.cn[1];
12200 var contentCfg = cfg.cn[2];
12202 if(this.indicatorpos == 'right'){
12207 cls : 'control-label col-form-label',
12211 html : this.fieldLabel
12225 labelCfg = cfg.cn[0];
12226 contentCfg = cfg.cn[1];
12230 if(this.labelWidth > 12){
12231 labelCfg.style = "width: " + this.labelWidth + 'px';
12234 if(this.labelWidth < 13 && this.labelmd == 0){
12235 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12238 if(this.labellg > 0){
12239 labelCfg.cls += ' col-lg-' + this.labellg;
12240 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12243 if(this.labelmd > 0){
12244 labelCfg.cls += ' col-md-' + this.labelmd;
12245 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12248 if(this.labelsm > 0){
12249 labelCfg.cls += ' col-sm-' + this.labelsm;
12250 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12253 if(this.labelxs > 0){
12254 labelCfg.cls += ' col-xs-' + this.labelxs;
12255 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12259 } else if ( this.fieldLabel.length) {
12266 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12267 tooltip : 'This field is required',
12268 style : this.allowBlank ? ' display:none' : ''
12272 //cls : 'input-group-addon',
12273 html : this.fieldLabel
12281 if(this.indicatorpos == 'right'){
12286 //cls : 'input-group-addon',
12287 html : this.fieldLabel
12292 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12293 tooltip : 'This field is required',
12294 style : this.allowBlank ? ' display:none' : ''
12314 if (this.parentType === 'Navbar' && this.parent().bar) {
12315 cfg.cls += ' navbar-form';
12318 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12319 // on BS4 we do this only if not form
12320 cfg.cls += ' navbar-form';
12328 * return the real input element.
12330 inputEl: function ()
12332 return this.el.select('input.form-control',true).first();
12335 tooltipEl : function()
12337 return this.inputEl();
12340 indicatorEl : function()
12342 if (Roo.bootstrap.version == 4) {
12343 return false; // not enabled in v4 yet.
12346 var indicator = this.el.select('i.roo-required-indicator',true).first();
12356 setDisabled : function(v)
12358 var i = this.inputEl().dom;
12360 i.removeAttribute('disabled');
12364 i.setAttribute('disabled','true');
12366 initEvents : function()
12369 this.inputEl().on("keydown" , this.fireKey, this);
12370 this.inputEl().on("focus", this.onFocus, this);
12371 this.inputEl().on("blur", this.onBlur, this);
12373 this.inputEl().relayEvent('keyup', this);
12374 this.inputEl().relayEvent('paste', this);
12376 this.indicator = this.indicatorEl();
12378 if(this.indicator){
12379 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12382 // reference to original value for reset
12383 this.originalValue = this.getValue();
12384 //Roo.form.TextField.superclass.initEvents.call(this);
12385 if(this.validationEvent == 'keyup'){
12386 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12387 this.inputEl().on('keyup', this.filterValidation, this);
12389 else if(this.validationEvent !== false){
12390 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12393 if(this.selectOnFocus){
12394 this.on("focus", this.preFocus, this);
12397 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12398 this.inputEl().on("keypress", this.filterKeys, this);
12400 this.inputEl().relayEvent('keypress', this);
12403 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12404 this.el.on("click", this.autoSize, this);
12407 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12408 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12411 if (typeof(this.before) == 'object') {
12412 this.before.render(this.el.select('.roo-input-before',true).first());
12414 if (typeof(this.after) == 'object') {
12415 this.after.render(this.el.select('.roo-input-after',true).first());
12418 this.inputEl().on('change', this.onChange, this);
12421 filterValidation : function(e){
12422 if(!e.isNavKeyPress()){
12423 this.validationTask.delay(this.validationDelay);
12427 * Validates the field value
12428 * @return {Boolean} True if the value is valid, else false
12430 validate : function(){
12431 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12432 if(this.disabled || this.validateValue(this.getRawValue())){
12437 this.markInvalid();
12443 * Validates a value according to the field's validation rules and marks the field as invalid
12444 * if the validation fails
12445 * @param {Mixed} value The value to validate
12446 * @return {Boolean} True if the value is valid, else false
12448 validateValue : function(value)
12450 if(this.getVisibilityEl().hasClass('hidden')){
12454 if(value.length < 1) { // if it's blank
12455 if(this.allowBlank){
12461 if(value.length < this.minLength){
12464 if(value.length > this.maxLength){
12468 var vt = Roo.form.VTypes;
12469 if(!vt[this.vtype](value, this)){
12473 if(typeof this.validator == "function"){
12474 var msg = this.validator(value);
12478 if (typeof(msg) == 'string') {
12479 this.invalidText = msg;
12483 if(this.regex && !this.regex.test(value)){
12491 fireKey : function(e){
12492 //Roo.log('field ' + e.getKey());
12493 if(e.isNavKeyPress()){
12494 this.fireEvent("specialkey", this, e);
12497 focus : function (selectText){
12499 this.inputEl().focus();
12500 if(selectText === true){
12501 this.inputEl().dom.select();
12507 onFocus : function(){
12508 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12509 // this.el.addClass(this.focusClass);
12511 if(!this.hasFocus){
12512 this.hasFocus = true;
12513 this.startValue = this.getValue();
12514 this.fireEvent("focus", this);
12518 beforeBlur : Roo.emptyFn,
12522 onBlur : function(){
12524 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12525 //this.el.removeClass(this.focusClass);
12527 this.hasFocus = false;
12528 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12531 var v = this.getValue();
12532 if(String(v) !== String(this.startValue)){
12533 this.fireEvent('change', this, v, this.startValue);
12535 this.fireEvent("blur", this);
12538 onChange : function(e)
12540 var v = this.getValue();
12541 if(String(v) !== String(this.startValue)){
12542 this.fireEvent('change', this, v, this.startValue);
12548 * Resets the current field value to the originally loaded value and clears any validation messages
12550 reset : function(){
12551 this.setValue(this.originalValue);
12555 * Returns the name of the field
12556 * @return {Mixed} name The name field
12558 getName: function(){
12562 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12563 * @return {Mixed} value The field value
12565 getValue : function(){
12567 var v = this.inputEl().getValue();
12572 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12573 * @return {Mixed} value The field value
12575 getRawValue : function(){
12576 var v = this.inputEl().getValue();
12582 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12583 * @param {Mixed} value The value to set
12585 setRawValue : function(v){
12586 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12589 selectText : function(start, end){
12590 var v = this.getRawValue();
12592 start = start === undefined ? 0 : start;
12593 end = end === undefined ? v.length : end;
12594 var d = this.inputEl().dom;
12595 if(d.setSelectionRange){
12596 d.setSelectionRange(start, end);
12597 }else if(d.createTextRange){
12598 var range = d.createTextRange();
12599 range.moveStart("character", start);
12600 range.moveEnd("character", v.length-end);
12607 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12608 * @param {Mixed} value The value to set
12610 setValue : function(v){
12613 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12619 processValue : function(value){
12620 if(this.stripCharsRe){
12621 var newValue = value.replace(this.stripCharsRe, '');
12622 if(newValue !== value){
12623 this.setRawValue(newValue);
12630 preFocus : function(){
12632 if(this.selectOnFocus){
12633 this.inputEl().dom.select();
12636 filterKeys : function(e){
12637 var k = e.getKey();
12638 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12641 var c = e.getCharCode(), cc = String.fromCharCode(c);
12642 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12645 if(!this.maskRe.test(cc)){
12650 * Clear any invalid styles/messages for this field
12652 clearInvalid : function(){
12654 if(!this.el || this.preventMark){ // not rendered
12659 this.el.removeClass([this.invalidClass, 'is-invalid']);
12661 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12663 var feedback = this.el.select('.form-control-feedback', true).first();
12666 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12671 if(this.indicator){
12672 this.indicator.removeClass('visible');
12673 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12676 this.fireEvent('valid', this);
12680 * Mark this field as valid
12682 markValid : function()
12684 if(!this.el || this.preventMark){ // not rendered...
12688 this.el.removeClass([this.invalidClass, this.validClass]);
12689 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12691 var feedback = this.el.select('.form-control-feedback', true).first();
12694 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12697 if(this.indicator){
12698 this.indicator.removeClass('visible');
12699 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12707 if(this.allowBlank && !this.getRawValue().length){
12710 if (Roo.bootstrap.version == 3) {
12711 this.el.addClass(this.validClass);
12713 this.inputEl().addClass('is-valid');
12716 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12718 var feedback = this.el.select('.form-control-feedback', true).first();
12721 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12722 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12727 this.fireEvent('valid', this);
12731 * Mark this field as invalid
12732 * @param {String} msg The validation message
12734 markInvalid : function(msg)
12736 if(!this.el || this.preventMark){ // not rendered
12740 this.el.removeClass([this.invalidClass, this.validClass]);
12741 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12743 var feedback = this.el.select('.form-control-feedback', true).first();
12746 this.el.select('.form-control-feedback', true).first().removeClass(
12747 [this.invalidFeedbackClass, this.validFeedbackClass]);
12754 if(this.allowBlank && !this.getRawValue().length){
12758 if(this.indicator){
12759 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12760 this.indicator.addClass('visible');
12762 if (Roo.bootstrap.version == 3) {
12763 this.el.addClass(this.invalidClass);
12765 this.inputEl().addClass('is-invalid');
12770 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12772 var feedback = this.el.select('.form-control-feedback', true).first();
12775 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12777 if(this.getValue().length || this.forceFeedback){
12778 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12785 this.fireEvent('invalid', this, msg);
12788 SafariOnKeyDown : function(event)
12790 // this is a workaround for a password hang bug on chrome/ webkit.
12791 if (this.inputEl().dom.type != 'password') {
12795 var isSelectAll = false;
12797 if(this.inputEl().dom.selectionEnd > 0){
12798 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12800 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12801 event.preventDefault();
12806 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12808 event.preventDefault();
12809 // this is very hacky as keydown always get's upper case.
12811 var cc = String.fromCharCode(event.getCharCode());
12812 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12816 adjustWidth : function(tag, w){
12817 tag = tag.toLowerCase();
12818 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12819 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12820 if(tag == 'input'){
12823 if(tag == 'textarea'){
12826 }else if(Roo.isOpera){
12827 if(tag == 'input'){
12830 if(tag == 'textarea'){
12838 setFieldLabel : function(v)
12840 if(!this.rendered){
12844 if(this.indicatorEl()){
12845 var ar = this.el.select('label > span',true);
12847 if (ar.elements.length) {
12848 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12849 this.fieldLabel = v;
12853 var br = this.el.select('label',true);
12855 if(br.elements.length) {
12856 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12857 this.fieldLabel = v;
12861 Roo.log('Cannot Found any of label > span || label in input');
12865 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12866 this.fieldLabel = v;
12881 * @class Roo.bootstrap.TextArea
12882 * @extends Roo.bootstrap.Input
12883 * Bootstrap TextArea class
12884 * @cfg {Number} cols Specifies the visible width of a text area
12885 * @cfg {Number} rows Specifies the visible number of lines in a text area
12886 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12887 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12888 * @cfg {string} html text
12891 * Create a new TextArea
12892 * @param {Object} config The config object
12895 Roo.bootstrap.TextArea = function(config){
12896 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12900 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12910 getAutoCreate : function(){
12912 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12918 if(this.inputType != 'hidden'){
12919 cfg.cls = 'form-group' //input-group
12927 value : this.value || '',
12928 html: this.html || '',
12929 cls : 'form-control',
12930 placeholder : this.placeholder || ''
12934 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12935 input.maxLength = this.maxLength;
12939 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12943 input.cols = this.cols;
12946 if (this.readOnly) {
12947 input.readonly = true;
12951 input.name = this.name;
12955 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12959 ['xs','sm','md','lg'].map(function(size){
12960 if (settings[size]) {
12961 cfg.cls += ' col-' + size + '-' + settings[size];
12965 var inputblock = input;
12967 if(this.hasFeedback && !this.allowBlank){
12971 cls: 'glyphicon form-control-feedback'
12975 cls : 'has-feedback',
12984 if (this.before || this.after) {
12987 cls : 'input-group',
12991 inputblock.cn.push({
12993 cls : 'input-group-addon',
12998 inputblock.cn.push(input);
13000 if(this.hasFeedback && !this.allowBlank){
13001 inputblock.cls += ' has-feedback';
13002 inputblock.cn.push(feedback);
13006 inputblock.cn.push({
13008 cls : 'input-group-addon',
13015 if (align ==='left' && this.fieldLabel.length) {
13020 cls : 'control-label',
13021 html : this.fieldLabel
13032 if(this.labelWidth > 12){
13033 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13036 if(this.labelWidth < 13 && this.labelmd == 0){
13037 this.labelmd = this.labelWidth;
13040 if(this.labellg > 0){
13041 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13042 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13045 if(this.labelmd > 0){
13046 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13047 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13050 if(this.labelsm > 0){
13051 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13052 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13055 if(this.labelxs > 0){
13056 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13057 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13060 } else if ( this.fieldLabel.length) {
13065 //cls : 'input-group-addon',
13066 html : this.fieldLabel
13084 if (this.disabled) {
13085 input.disabled=true;
13092 * return the real textarea element.
13094 inputEl: function ()
13096 return this.el.select('textarea.form-control',true).first();
13100 * Clear any invalid styles/messages for this field
13102 clearInvalid : function()
13105 if(!this.el || this.preventMark){ // not rendered
13109 var label = this.el.select('label', true).first();
13110 var icon = this.el.select('i.fa-star', true).first();
13115 this.el.removeClass( this.validClass);
13116 this.inputEl().removeClass('is-invalid');
13118 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13120 var feedback = this.el.select('.form-control-feedback', true).first();
13123 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13128 this.fireEvent('valid', this);
13132 * Mark this field as valid
13134 markValid : function()
13136 if(!this.el || this.preventMark){ // not rendered
13140 this.el.removeClass([this.invalidClass, this.validClass]);
13141 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13143 var feedback = this.el.select('.form-control-feedback', true).first();
13146 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13149 if(this.disabled || this.allowBlank){
13153 var label = this.el.select('label', true).first();
13154 var icon = this.el.select('i.fa-star', true).first();
13159 if (Roo.bootstrap.version == 3) {
13160 this.el.addClass(this.validClass);
13162 this.inputEl().addClass('is-valid');
13166 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13168 var feedback = this.el.select('.form-control-feedback', true).first();
13171 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13172 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13177 this.fireEvent('valid', this);
13181 * Mark this field as invalid
13182 * @param {String} msg The validation message
13184 markInvalid : function(msg)
13186 if(!this.el || this.preventMark){ // not rendered
13190 this.el.removeClass([this.invalidClass, this.validClass]);
13191 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13193 var feedback = this.el.select('.form-control-feedback', true).first();
13196 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13199 if(this.disabled || this.allowBlank){
13203 var label = this.el.select('label', true).first();
13204 var icon = this.el.select('i.fa-star', true).first();
13206 if(!this.getValue().length && label && !icon){
13207 this.el.createChild({
13209 cls : 'text-danger fa fa-lg fa-star',
13210 tooltip : 'This field is required',
13211 style : 'margin-right:5px;'
13215 if (Roo.bootstrap.version == 3) {
13216 this.el.addClass(this.invalidClass);
13218 this.inputEl().addClass('is-invalid');
13221 // fixme ... this may be depricated need to test..
13222 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13224 var feedback = this.el.select('.form-control-feedback', true).first();
13227 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13229 if(this.getValue().length || this.forceFeedback){
13230 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13237 this.fireEvent('invalid', this, msg);
13245 * trigger field - base class for combo..
13250 * @class Roo.bootstrap.TriggerField
13251 * @extends Roo.bootstrap.Input
13252 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13253 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13254 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13255 * for which you can provide a custom implementation. For example:
13257 var trigger = new Roo.bootstrap.TriggerField();
13258 trigger.onTriggerClick = myTriggerFn;
13259 trigger.applyTo('my-field');
13262 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13263 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13264 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13265 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13266 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13269 * Create a new TriggerField.
13270 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13271 * to the base TextField)
13273 Roo.bootstrap.TriggerField = function(config){
13274 this.mimicing = false;
13275 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13278 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13280 * @cfg {String} triggerClass A CSS class to apply to the trigger
13283 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13288 * @cfg {Boolean} removable (true|false) special filter default false
13292 /** @cfg {Boolean} grow @hide */
13293 /** @cfg {Number} growMin @hide */
13294 /** @cfg {Number} growMax @hide */
13300 autoSize: Roo.emptyFn,
13304 deferHeight : true,
13307 actionMode : 'wrap',
13312 getAutoCreate : function(){
13314 var align = this.labelAlign || this.parentLabelAlign();
13319 cls: 'form-group' //input-group
13326 type : this.inputType,
13327 cls : 'form-control',
13328 autocomplete: 'new-password',
13329 placeholder : this.placeholder || ''
13333 input.name = this.name;
13336 input.cls += ' input-' + this.size;
13339 if (this.disabled) {
13340 input.disabled=true;
13343 var inputblock = input;
13345 if(this.hasFeedback && !this.allowBlank){
13349 cls: 'glyphicon form-control-feedback'
13352 if(this.removable && !this.editable ){
13354 cls : 'has-feedback',
13360 cls : 'roo-combo-removable-btn close'
13367 cls : 'has-feedback',
13376 if(this.removable && !this.editable ){
13378 cls : 'roo-removable',
13384 cls : 'roo-combo-removable-btn close'
13391 if (this.before || this.after) {
13394 cls : 'input-group',
13398 inputblock.cn.push({
13400 cls : 'input-group-addon input-group-prepend input-group-text',
13405 inputblock.cn.push(input);
13407 if(this.hasFeedback && !this.allowBlank){
13408 inputblock.cls += ' has-feedback';
13409 inputblock.cn.push(feedback);
13413 inputblock.cn.push({
13415 cls : 'input-group-addon input-group-append input-group-text',
13424 var ibwrap = inputblock;
13429 cls: 'roo-select2-choices',
13433 cls: 'roo-select2-search-field',
13445 cls: 'roo-select2-container input-group',
13450 cls: 'form-hidden-field'
13456 if(!this.multiple && this.showToggleBtn){
13462 if (this.caret != false) {
13465 cls: 'fa fa-' + this.caret
13472 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13474 Roo.bootstrap.version == 3 ? caret : '',
13477 cls: 'combobox-clear',
13491 combobox.cls += ' roo-select2-container-multi';
13495 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13496 tooltip : 'This field is required'
13498 if (Roo.bootstrap.version == 4) {
13501 style : 'display:none'
13506 if (align ==='left' && this.fieldLabel.length) {
13508 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13515 cls : 'control-label',
13516 html : this.fieldLabel
13528 var labelCfg = cfg.cn[1];
13529 var contentCfg = cfg.cn[2];
13531 if(this.indicatorpos == 'right'){
13536 cls : 'control-label',
13540 html : this.fieldLabel
13554 labelCfg = cfg.cn[0];
13555 contentCfg = cfg.cn[1];
13558 if(this.labelWidth > 12){
13559 labelCfg.style = "width: " + this.labelWidth + 'px';
13562 if(this.labelWidth < 13 && this.labelmd == 0){
13563 this.labelmd = this.labelWidth;
13566 if(this.labellg > 0){
13567 labelCfg.cls += ' col-lg-' + this.labellg;
13568 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13571 if(this.labelmd > 0){
13572 labelCfg.cls += ' col-md-' + this.labelmd;
13573 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13576 if(this.labelsm > 0){
13577 labelCfg.cls += ' col-sm-' + this.labelsm;
13578 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13581 if(this.labelxs > 0){
13582 labelCfg.cls += ' col-xs-' + this.labelxs;
13583 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13586 } else if ( this.fieldLabel.length) {
13587 // Roo.log(" label");
13592 //cls : 'input-group-addon',
13593 html : this.fieldLabel
13601 if(this.indicatorpos == 'right'){
13609 html : this.fieldLabel
13623 // Roo.log(" no label && no align");
13630 ['xs','sm','md','lg'].map(function(size){
13631 if (settings[size]) {
13632 cfg.cls += ' col-' + size + '-' + settings[size];
13643 onResize : function(w, h){
13644 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13645 // if(typeof w == 'number'){
13646 // var x = w - this.trigger.getWidth();
13647 // this.inputEl().setWidth(this.adjustWidth('input', x));
13648 // this.trigger.setStyle('left', x+'px');
13653 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13656 getResizeEl : function(){
13657 return this.inputEl();
13661 getPositionEl : function(){
13662 return this.inputEl();
13666 alignErrorIcon : function(){
13667 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13671 initEvents : function(){
13675 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13676 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13677 if(!this.multiple && this.showToggleBtn){
13678 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13679 if(this.hideTrigger){
13680 this.trigger.setDisplayed(false);
13682 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13686 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13689 if(this.removable && !this.editable && !this.tickable){
13690 var close = this.closeTriggerEl();
13693 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13694 close.on('click', this.removeBtnClick, this, close);
13698 //this.trigger.addClassOnOver('x-form-trigger-over');
13699 //this.trigger.addClassOnClick('x-form-trigger-click');
13702 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13706 closeTriggerEl : function()
13708 var close = this.el.select('.roo-combo-removable-btn', true).first();
13709 return close ? close : false;
13712 removeBtnClick : function(e, h, el)
13714 e.preventDefault();
13716 if(this.fireEvent("remove", this) !== false){
13718 this.fireEvent("afterremove", this)
13722 createList : function()
13724 this.list = Roo.get(document.body).createChild({
13725 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13726 cls: 'typeahead typeahead-long dropdown-menu shadow',
13727 style: 'display:none'
13730 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13735 initTrigger : function(){
13740 onDestroy : function(){
13742 this.trigger.removeAllListeners();
13743 // this.trigger.remove();
13746 // this.wrap.remove();
13748 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13752 onFocus : function(){
13753 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13755 if(!this.mimicing){
13756 this.wrap.addClass('x-trigger-wrap-focus');
13757 this.mimicing = true;
13758 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13759 if(this.monitorTab){
13760 this.el.on("keydown", this.checkTab, this);
13767 checkTab : function(e){
13768 if(e.getKey() == e.TAB){
13769 this.triggerBlur();
13774 onBlur : function(){
13779 mimicBlur : function(e, t){
13781 if(!this.wrap.contains(t) && this.validateBlur()){
13782 this.triggerBlur();
13788 triggerBlur : function(){
13789 this.mimicing = false;
13790 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13791 if(this.monitorTab){
13792 this.el.un("keydown", this.checkTab, this);
13794 //this.wrap.removeClass('x-trigger-wrap-focus');
13795 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13799 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13800 validateBlur : function(e, t){
13805 onDisable : function(){
13806 this.inputEl().dom.disabled = true;
13807 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13809 // this.wrap.addClass('x-item-disabled');
13814 onEnable : function(){
13815 this.inputEl().dom.disabled = false;
13816 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13818 // this.el.removeClass('x-item-disabled');
13823 onShow : function(){
13824 var ae = this.getActionEl();
13827 ae.dom.style.display = '';
13828 ae.dom.style.visibility = 'visible';
13834 onHide : function(){
13835 var ae = this.getActionEl();
13836 ae.dom.style.display = 'none';
13840 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13841 * by an implementing function.
13843 * @param {EventObject} e
13845 onTriggerClick : Roo.emptyFn
13853 * @class Roo.bootstrap.CardUploader
13854 * @extends Roo.bootstrap.Button
13855 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13856 * @cfg {Number} errorTimeout default 3000
13857 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13858 * @cfg {Array} html The button text.
13862 * Create a new CardUploader
13863 * @param {Object} config The config object
13866 Roo.bootstrap.CardUploader = function(config){
13870 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13873 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13881 * When a image is clicked on - and needs to display a slideshow or similar..
13882 * @param {Roo.bootstrap.Card} this
13883 * @param {Object} The image information data
13889 * When a the download link is clicked
13890 * @param {Roo.bootstrap.Card} this
13891 * @param {Object} The image information data contains
13898 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13901 errorTimeout : 3000,
13905 fileCollection : false,
13908 getAutoCreate : function()
13912 cls :'form-group' ,
13917 //cls : 'input-group-addon',
13918 html : this.fieldLabel
13926 value : this.value,
13927 cls : 'd-none form-control'
13932 multiple : 'multiple',
13934 cls : 'd-none roo-card-upload-selector'
13938 cls : 'roo-card-uploader-button-container w-100 mb-2'
13941 cls : 'card-columns roo-card-uploader-container'
13951 getChildContainer : function() /// what children are added to.
13953 return this.containerEl;
13956 getButtonContainer : function() /// what children are added to.
13958 return this.el.select(".roo-card-uploader-button-container").first();
13961 initEvents : function()
13964 Roo.bootstrap.Input.prototype.initEvents.call(this);
13968 xns: Roo.bootstrap,
13971 container_method : 'getButtonContainer' ,
13972 html : this.html, // fix changable?
13975 'click' : function(btn, e) {
13984 this.urlAPI = (window.createObjectURL && window) ||
13985 (window.URL && URL.revokeObjectURL && URL) ||
13986 (window.webkitURL && webkitURL);
13991 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13993 this.selectorEl.on('change', this.onFileSelected, this);
13996 this.images.forEach(function(img) {
13999 this.images = false;
14001 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14007 onClick : function(e)
14009 e.preventDefault();
14011 this.selectorEl.dom.click();
14015 onFileSelected : function(e)
14017 e.preventDefault();
14019 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14023 Roo.each(this.selectorEl.dom.files, function(file){
14024 this.addFile(file);
14033 addFile : function(file)
14036 if(typeof(file) === 'string'){
14037 throw "Add file by name?"; // should not happen
14041 if(!file || !this.urlAPI){
14051 var url = _this.urlAPI.createObjectURL( file);
14054 id : Roo.bootstrap.CardUploader.ID--,
14055 is_uploaded : false,
14059 mimetype : file.type,
14067 * addCard - add an Attachment to the uploader
14068 * @param data - the data about the image to upload
14072 title : "Title of file",
14073 is_uploaded : false,
14074 src : "http://.....",
14075 srcfile : { the File upload object },
14076 mimetype : file.type,
14079 .. any other data...
14085 addCard : function (data)
14087 // hidden input element?
14088 // if the file is not an image...
14089 //then we need to use something other that and header_image
14094 xns : Roo.bootstrap,
14095 xtype : 'CardFooter',
14098 xns : Roo.bootstrap,
14104 xns : Roo.bootstrap,
14106 html : String.format("<small>{0}</small>", data.title),
14107 cls : 'col-10 text-left',
14112 click : function() {
14114 t.fireEvent( "download", t, data );
14120 xns : Roo.bootstrap,
14122 style: 'max-height: 28px; ',
14128 click : function() {
14129 t.removeCard(data.id)
14141 var cn = this.addxtype(
14144 xns : Roo.bootstrap,
14147 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14148 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14149 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14154 initEvents : function() {
14155 Roo.bootstrap.Card.prototype.initEvents.call(this);
14157 this.imgEl = this.el.select('.card-img-top').first();
14159 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14160 this.imgEl.set({ 'pointer' : 'cursor' });
14163 this.getCardFooter().addClass('p-1');
14170 // dont' really need ot update items.
14171 // this.items.push(cn);
14172 this.fileCollection.add(cn);
14174 if (!data.srcfile) {
14175 this.updateInput();
14180 var reader = new FileReader();
14181 reader.addEventListener("load", function() {
14182 data.srcdata = reader.result;
14185 reader.readAsDataURL(data.srcfile);
14190 removeCard : function(id)
14193 var card = this.fileCollection.get(id);
14194 card.data.is_deleted = 1;
14195 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14196 //this.fileCollection.remove(card);
14197 //this.items = this.items.filter(function(e) { return e != card });
14198 // dont' really need ot update items.
14199 card.el.dom.parentNode.removeChild(card.el.dom);
14200 this.updateInput();
14206 this.fileCollection.each(function(card) {
14207 if (card.el.dom && card.el.dom.parentNode) {
14208 card.el.dom.parentNode.removeChild(card.el.dom);
14211 this.fileCollection.clear();
14212 this.updateInput();
14215 updateInput : function()
14218 this.fileCollection.each(function(e) {
14222 this.inputEl().dom.value = JSON.stringify(data);
14232 Roo.bootstrap.CardUploader.ID = -1;/*
14234 * Ext JS Library 1.1.1
14235 * Copyright(c) 2006-2007, Ext JS, LLC.
14237 * Originally Released Under LGPL - original licence link has changed is not relivant.
14240 * <script type="text/javascript">
14245 * @class Roo.data.SortTypes
14247 * Defines the default sorting (casting?) comparison functions used when sorting data.
14249 Roo.data.SortTypes = {
14251 * Default sort that does nothing
14252 * @param {Mixed} s The value being converted
14253 * @return {Mixed} The comparison value
14255 none : function(s){
14260 * The regular expression used to strip tags
14264 stripTagsRE : /<\/?[^>]+>/gi,
14267 * Strips all HTML tags to sort on text only
14268 * @param {Mixed} s The value being converted
14269 * @return {String} The comparison value
14271 asText : function(s){
14272 return String(s).replace(this.stripTagsRE, "");
14276 * Strips all HTML tags to sort on text only - Case insensitive
14277 * @param {Mixed} s The value being converted
14278 * @return {String} The comparison value
14280 asUCText : function(s){
14281 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14285 * Case insensitive string
14286 * @param {Mixed} s The value being converted
14287 * @return {String} The comparison value
14289 asUCString : function(s) {
14290 return String(s).toUpperCase();
14295 * @param {Mixed} s The value being converted
14296 * @return {Number} The comparison value
14298 asDate : function(s) {
14302 if(s instanceof Date){
14303 return s.getTime();
14305 return Date.parse(String(s));
14310 * @param {Mixed} s The value being converted
14311 * @return {Float} The comparison value
14313 asFloat : function(s) {
14314 var val = parseFloat(String(s).replace(/,/g, ""));
14323 * @param {Mixed} s The value being converted
14324 * @return {Number} The comparison value
14326 asInt : function(s) {
14327 var val = parseInt(String(s).replace(/,/g, ""));
14335 * Ext JS Library 1.1.1
14336 * Copyright(c) 2006-2007, Ext JS, LLC.
14338 * Originally Released Under LGPL - original licence link has changed is not relivant.
14341 * <script type="text/javascript">
14345 * @class Roo.data.Record
14346 * Instances of this class encapsulate both record <em>definition</em> information, and record
14347 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14348 * to access Records cached in an {@link Roo.data.Store} object.<br>
14350 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14351 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14354 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14356 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14357 * {@link #create}. The parameters are the same.
14358 * @param {Array} data An associative Array of data values keyed by the field name.
14359 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14360 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14361 * not specified an integer id is generated.
14363 Roo.data.Record = function(data, id){
14364 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14369 * Generate a constructor for a specific record layout.
14370 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14371 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14372 * Each field definition object may contain the following properties: <ul>
14373 * <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,
14374 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14375 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14376 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14377 * is being used, then this is a string containing the javascript expression to reference the data relative to
14378 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14379 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14380 * this may be omitted.</p></li>
14381 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14382 * <ul><li>auto (Default, implies no conversion)</li>
14387 * <li>date</li></ul></p></li>
14388 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14389 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14390 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14391 * by the Reader into an object that will be stored in the Record. It is passed the
14392 * following parameters:<ul>
14393 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14395 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14397 * <br>usage:<br><pre><code>
14398 var TopicRecord = Roo.data.Record.create(
14399 {name: 'title', mapping: 'topic_title'},
14400 {name: 'author', mapping: 'username'},
14401 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14402 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14403 {name: 'lastPoster', mapping: 'user2'},
14404 {name: 'excerpt', mapping: 'post_text'}
14407 var myNewRecord = new TopicRecord({
14408 title: 'Do my job please',
14411 lastPost: new Date(),
14412 lastPoster: 'Animal',
14413 excerpt: 'No way dude!'
14415 myStore.add(myNewRecord);
14420 Roo.data.Record.create = function(o){
14421 var f = function(){
14422 f.superclass.constructor.apply(this, arguments);
14424 Roo.extend(f, Roo.data.Record);
14425 var p = f.prototype;
14426 p.fields = new Roo.util.MixedCollection(false, function(field){
14429 for(var i = 0, len = o.length; i < len; i++){
14430 p.fields.add(new Roo.data.Field(o[i]));
14432 f.getField = function(name){
14433 return p.fields.get(name);
14438 Roo.data.Record.AUTO_ID = 1000;
14439 Roo.data.Record.EDIT = 'edit';
14440 Roo.data.Record.REJECT = 'reject';
14441 Roo.data.Record.COMMIT = 'commit';
14443 Roo.data.Record.prototype = {
14445 * Readonly flag - true if this record has been modified.
14454 join : function(store){
14455 this.store = store;
14459 * Set the named field to the specified value.
14460 * @param {String} name The name of the field to set.
14461 * @param {Object} value The value to set the field to.
14463 set : function(name, value){
14464 if(this.data[name] == value){
14468 if(!this.modified){
14469 this.modified = {};
14471 if(typeof this.modified[name] == 'undefined'){
14472 this.modified[name] = this.data[name];
14474 this.data[name] = value;
14475 if(!this.editing && this.store){
14476 this.store.afterEdit(this);
14481 * Get the value of the named field.
14482 * @param {String} name The name of the field to get the value of.
14483 * @return {Object} The value of the field.
14485 get : function(name){
14486 return this.data[name];
14490 beginEdit : function(){
14491 this.editing = true;
14492 this.modified = {};
14496 cancelEdit : function(){
14497 this.editing = false;
14498 delete this.modified;
14502 endEdit : function(){
14503 this.editing = false;
14504 if(this.dirty && this.store){
14505 this.store.afterEdit(this);
14510 * Usually called by the {@link Roo.data.Store} which owns the Record.
14511 * Rejects all changes made to the Record since either creation, or the last commit operation.
14512 * Modified fields are reverted to their original values.
14514 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14515 * of reject operations.
14517 reject : function(){
14518 var m = this.modified;
14520 if(typeof m[n] != "function"){
14521 this.data[n] = m[n];
14524 this.dirty = false;
14525 delete this.modified;
14526 this.editing = false;
14528 this.store.afterReject(this);
14533 * Usually called by the {@link Roo.data.Store} which owns the Record.
14534 * Commits all changes made to the Record since either creation, or the last commit operation.
14536 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14537 * of commit operations.
14539 commit : function(){
14540 this.dirty = false;
14541 delete this.modified;
14542 this.editing = false;
14544 this.store.afterCommit(this);
14549 hasError : function(){
14550 return this.error != null;
14554 clearError : function(){
14559 * Creates a copy of this record.
14560 * @param {String} id (optional) A new record id if you don't want to use this record's id
14563 copy : function(newId) {
14564 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14568 * Ext JS Library 1.1.1
14569 * Copyright(c) 2006-2007, Ext JS, LLC.
14571 * Originally Released Under LGPL - original licence link has changed is not relivant.
14574 * <script type="text/javascript">
14580 * @class Roo.data.Store
14581 * @extends Roo.util.Observable
14582 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14583 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14585 * 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
14586 * has no knowledge of the format of the data returned by the Proxy.<br>
14588 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14589 * instances from the data object. These records are cached and made available through accessor functions.
14591 * Creates a new Store.
14592 * @param {Object} config A config object containing the objects needed for the Store to access data,
14593 * and read the data into Records.
14595 Roo.data.Store = function(config){
14596 this.data = new Roo.util.MixedCollection(false);
14597 this.data.getKey = function(o){
14600 this.baseParams = {};
14602 this.paramNames = {
14607 "multisort" : "_multisort"
14610 if(config && config.data){
14611 this.inlineData = config.data;
14612 delete config.data;
14615 Roo.apply(this, config);
14617 if(this.reader){ // reader passed
14618 this.reader = Roo.factory(this.reader, Roo.data);
14619 this.reader.xmodule = this.xmodule || false;
14620 if(!this.recordType){
14621 this.recordType = this.reader.recordType;
14623 if(this.reader.onMetaChange){
14624 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14628 if(this.recordType){
14629 this.fields = this.recordType.prototype.fields;
14631 this.modified = [];
14635 * @event datachanged
14636 * Fires when the data cache has changed, and a widget which is using this Store
14637 * as a Record cache should refresh its view.
14638 * @param {Store} this
14640 datachanged : true,
14642 * @event metachange
14643 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14644 * @param {Store} this
14645 * @param {Object} meta The JSON metadata
14650 * Fires when Records have been added to the Store
14651 * @param {Store} this
14652 * @param {Roo.data.Record[]} records The array of Records added
14653 * @param {Number} index The index at which the record(s) were added
14658 * Fires when a Record has been removed from the Store
14659 * @param {Store} this
14660 * @param {Roo.data.Record} record The Record that was removed
14661 * @param {Number} index The index at which the record was removed
14666 * Fires when a Record has been updated
14667 * @param {Store} this
14668 * @param {Roo.data.Record} record The Record that was updated
14669 * @param {String} operation The update operation being performed. Value may be one of:
14671 Roo.data.Record.EDIT
14672 Roo.data.Record.REJECT
14673 Roo.data.Record.COMMIT
14679 * Fires when the data cache has been cleared.
14680 * @param {Store} this
14684 * @event beforeload
14685 * Fires before a request is made for a new data object. If the beforeload handler returns false
14686 * the load action will be canceled.
14687 * @param {Store} this
14688 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14692 * @event beforeloadadd
14693 * Fires after a new set of Records has been loaded.
14694 * @param {Store} this
14695 * @param {Roo.data.Record[]} records The Records that were loaded
14696 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14698 beforeloadadd : true,
14701 * Fires after a new set of Records has been loaded, before they are added to the store.
14702 * @param {Store} this
14703 * @param {Roo.data.Record[]} records The Records that were loaded
14704 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14705 * @params {Object} return from reader
14709 * @event loadexception
14710 * Fires if an exception occurs in the Proxy during loading.
14711 * Called with the signature of the Proxy's "loadexception" event.
14712 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14715 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14716 * @param {Object} load options
14717 * @param {Object} jsonData from your request (normally this contains the Exception)
14719 loadexception : true
14723 this.proxy = Roo.factory(this.proxy, Roo.data);
14724 this.proxy.xmodule = this.xmodule || false;
14725 this.relayEvents(this.proxy, ["loadexception"]);
14727 this.sortToggle = {};
14728 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14730 Roo.data.Store.superclass.constructor.call(this);
14732 if(this.inlineData){
14733 this.loadData(this.inlineData);
14734 delete this.inlineData;
14738 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14740 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14741 * without a remote query - used by combo/forms at present.
14745 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14748 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14751 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14752 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14755 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14756 * on any HTTP request
14759 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14762 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14766 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14767 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14769 remoteSort : false,
14772 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14773 * loaded or when a record is removed. (defaults to false).
14775 pruneModifiedRecords : false,
14778 lastOptions : null,
14781 * Add Records to the Store and fires the add event.
14782 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14784 add : function(records){
14785 records = [].concat(records);
14786 for(var i = 0, len = records.length; i < len; i++){
14787 records[i].join(this);
14789 var index = this.data.length;
14790 this.data.addAll(records);
14791 this.fireEvent("add", this, records, index);
14795 * Remove a Record from the Store and fires the remove event.
14796 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14798 remove : function(record){
14799 var index = this.data.indexOf(record);
14800 this.data.removeAt(index);
14802 if(this.pruneModifiedRecords){
14803 this.modified.remove(record);
14805 this.fireEvent("remove", this, record, index);
14809 * Remove all Records from the Store and fires the clear event.
14811 removeAll : function(){
14813 if(this.pruneModifiedRecords){
14814 this.modified = [];
14816 this.fireEvent("clear", this);
14820 * Inserts Records to the Store at the given index and fires the add event.
14821 * @param {Number} index The start index at which to insert the passed Records.
14822 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14824 insert : function(index, records){
14825 records = [].concat(records);
14826 for(var i = 0, len = records.length; i < len; i++){
14827 this.data.insert(index, records[i]);
14828 records[i].join(this);
14830 this.fireEvent("add", this, records, index);
14834 * Get the index within the cache of the passed Record.
14835 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14836 * @return {Number} The index of the passed Record. Returns -1 if not found.
14838 indexOf : function(record){
14839 return this.data.indexOf(record);
14843 * Get the index within the cache of the Record with the passed id.
14844 * @param {String} id The id of the Record to find.
14845 * @return {Number} The index of the Record. Returns -1 if not found.
14847 indexOfId : function(id){
14848 return this.data.indexOfKey(id);
14852 * Get the Record with the specified id.
14853 * @param {String} id The id of the Record to find.
14854 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14856 getById : function(id){
14857 return this.data.key(id);
14861 * Get the Record at the specified index.
14862 * @param {Number} index The index of the Record to find.
14863 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14865 getAt : function(index){
14866 return this.data.itemAt(index);
14870 * Returns a range of Records between specified indices.
14871 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14872 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14873 * @return {Roo.data.Record[]} An array of Records
14875 getRange : function(start, end){
14876 return this.data.getRange(start, end);
14880 storeOptions : function(o){
14881 o = Roo.apply({}, o);
14884 this.lastOptions = o;
14888 * Loads the Record cache from the configured Proxy using the configured Reader.
14890 * If using remote paging, then the first load call must specify the <em>start</em>
14891 * and <em>limit</em> properties in the options.params property to establish the initial
14892 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14894 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14895 * and this call will return before the new data has been loaded. Perform any post-processing
14896 * in a callback function, or in a "load" event handler.</strong>
14898 * @param {Object} options An object containing properties which control loading options:<ul>
14899 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14900 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14901 * passed the following arguments:<ul>
14902 * <li>r : Roo.data.Record[]</li>
14903 * <li>options: Options object from the load call</li>
14904 * <li>success: Boolean success indicator</li></ul></li>
14905 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14906 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14909 load : function(options){
14910 options = options || {};
14911 if(this.fireEvent("beforeload", this, options) !== false){
14912 this.storeOptions(options);
14913 var p = Roo.apply(options.params || {}, this.baseParams);
14914 // if meta was not loaded from remote source.. try requesting it.
14915 if (!this.reader.metaFromRemote) {
14916 p._requestMeta = 1;
14918 if(this.sortInfo && this.remoteSort){
14919 var pn = this.paramNames;
14920 p[pn["sort"]] = this.sortInfo.field;
14921 p[pn["dir"]] = this.sortInfo.direction;
14923 if (this.multiSort) {
14924 var pn = this.paramNames;
14925 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14928 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14933 * Reloads the Record cache from the configured Proxy using the configured Reader and
14934 * the options from the last load operation performed.
14935 * @param {Object} options (optional) An object containing properties which may override the options
14936 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14937 * the most recently used options are reused).
14939 reload : function(options){
14940 this.load(Roo.applyIf(options||{}, this.lastOptions));
14944 // Called as a callback by the Reader during a load operation.
14945 loadRecords : function(o, options, success){
14946 if(!o || success === false){
14947 if(success !== false){
14948 this.fireEvent("load", this, [], options, o);
14950 if(options.callback){
14951 options.callback.call(options.scope || this, [], options, false);
14955 // if data returned failure - throw an exception.
14956 if (o.success === false) {
14957 // show a message if no listener is registered.
14958 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14959 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14961 // loadmask wil be hooked into this..
14962 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14965 var r = o.records, t = o.totalRecords || r.length;
14967 this.fireEvent("beforeloadadd", this, r, options, o);
14969 if(!options || options.add !== true){
14970 if(this.pruneModifiedRecords){
14971 this.modified = [];
14973 for(var i = 0, len = r.length; i < len; i++){
14977 this.data = this.snapshot;
14978 delete this.snapshot;
14981 this.data.addAll(r);
14982 this.totalLength = t;
14984 this.fireEvent("datachanged", this);
14986 this.totalLength = Math.max(t, this.data.length+r.length);
14990 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14992 var e = new Roo.data.Record({});
14994 e.set(this.parent.displayField, this.parent.emptyTitle);
14995 e.set(this.parent.valueField, '');
15000 this.fireEvent("load", this, r, options, o);
15001 if(options.callback){
15002 options.callback.call(options.scope || this, r, options, true);
15008 * Loads data from a passed data block. A Reader which understands the format of the data
15009 * must have been configured in the constructor.
15010 * @param {Object} data The data block from which to read the Records. The format of the data expected
15011 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15012 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15014 loadData : function(o, append){
15015 var r = this.reader.readRecords(o);
15016 this.loadRecords(r, {add: append}, true);
15020 * using 'cn' the nested child reader read the child array into it's child stores.
15021 * @param {Object} rec The record with a 'children array
15023 loadDataFromChildren : function(rec)
15025 this.loadData(this.reader.toLoadData(rec));
15030 * Gets the number of cached records.
15032 * <em>If using paging, this may not be the total size of the dataset. If the data object
15033 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15034 * the data set size</em>
15036 getCount : function(){
15037 return this.data.length || 0;
15041 * Gets the total number of records in the dataset as returned by the server.
15043 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15044 * the dataset size</em>
15046 getTotalCount : function(){
15047 return this.totalLength || 0;
15051 * Returns the sort state of the Store as an object with two properties:
15053 field {String} The name of the field by which the Records are sorted
15054 direction {String} The sort order, "ASC" or "DESC"
15057 getSortState : function(){
15058 return this.sortInfo;
15062 applySort : function(){
15063 if(this.sortInfo && !this.remoteSort){
15064 var s = this.sortInfo, f = s.field;
15065 var st = this.fields.get(f).sortType;
15066 var fn = function(r1, r2){
15067 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15068 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15070 this.data.sort(s.direction, fn);
15071 if(this.snapshot && this.snapshot != this.data){
15072 this.snapshot.sort(s.direction, fn);
15078 * Sets the default sort column and order to be used by the next load operation.
15079 * @param {String} fieldName The name of the field to sort by.
15080 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15082 setDefaultSort : function(field, dir){
15083 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15087 * Sort the Records.
15088 * If remote sorting is used, the sort is performed on the server, and the cache is
15089 * reloaded. If local sorting is used, the cache is sorted internally.
15090 * @param {String} fieldName The name of the field to sort by.
15091 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15093 sort : function(fieldName, dir){
15094 var f = this.fields.get(fieldName);
15096 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15098 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15099 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15104 this.sortToggle[f.name] = dir;
15105 this.sortInfo = {field: f.name, direction: dir};
15106 if(!this.remoteSort){
15108 this.fireEvent("datachanged", this);
15110 this.load(this.lastOptions);
15115 * Calls the specified function for each of the Records in the cache.
15116 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15117 * Returning <em>false</em> aborts and exits the iteration.
15118 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15120 each : function(fn, scope){
15121 this.data.each(fn, scope);
15125 * Gets all records modified since the last commit. Modified records are persisted across load operations
15126 * (e.g., during paging).
15127 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15129 getModifiedRecords : function(){
15130 return this.modified;
15134 createFilterFn : function(property, value, anyMatch){
15135 if(!value.exec){ // not a regex
15136 value = String(value);
15137 if(value.length == 0){
15140 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15142 return function(r){
15143 return value.test(r.data[property]);
15148 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15149 * @param {String} property A field on your records
15150 * @param {Number} start The record index to start at (defaults to 0)
15151 * @param {Number} end The last record index to include (defaults to length - 1)
15152 * @return {Number} The sum
15154 sum : function(property, start, end){
15155 var rs = this.data.items, v = 0;
15156 start = start || 0;
15157 end = (end || end === 0) ? end : rs.length-1;
15159 for(var i = start; i <= end; i++){
15160 v += (rs[i].data[property] || 0);
15166 * Filter the records by a specified property.
15167 * @param {String} field A field on your records
15168 * @param {String/RegExp} value Either a string that the field
15169 * should start with or a RegExp to test against the field
15170 * @param {Boolean} anyMatch True to match any part not just the beginning
15172 filter : function(property, value, anyMatch){
15173 var fn = this.createFilterFn(property, value, anyMatch);
15174 return fn ? this.filterBy(fn) : this.clearFilter();
15178 * Filter by a function. The specified function will be called with each
15179 * record in this data source. If the function returns true the record is included,
15180 * otherwise it is filtered.
15181 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15182 * @param {Object} scope (optional) The scope of the function (defaults to this)
15184 filterBy : function(fn, scope){
15185 this.snapshot = this.snapshot || this.data;
15186 this.data = this.queryBy(fn, scope||this);
15187 this.fireEvent("datachanged", this);
15191 * Query the records by a specified property.
15192 * @param {String} field A field on your records
15193 * @param {String/RegExp} value Either a string that the field
15194 * should start with or a RegExp to test against the field
15195 * @param {Boolean} anyMatch True to match any part not just the beginning
15196 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15198 query : function(property, value, anyMatch){
15199 var fn = this.createFilterFn(property, value, anyMatch);
15200 return fn ? this.queryBy(fn) : this.data.clone();
15204 * Query by a function. The specified function will be called with each
15205 * record in this data source. If the function returns true the record is included
15207 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15208 * @param {Object} scope (optional) The scope of the function (defaults to this)
15209 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15211 queryBy : function(fn, scope){
15212 var data = this.snapshot || this.data;
15213 return data.filterBy(fn, scope||this);
15217 * Collects unique values for a particular dataIndex from this store.
15218 * @param {String} dataIndex The property to collect
15219 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15220 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15221 * @return {Array} An array of the unique values
15223 collect : function(dataIndex, allowNull, bypassFilter){
15224 var d = (bypassFilter === true && this.snapshot) ?
15225 this.snapshot.items : this.data.items;
15226 var v, sv, r = [], l = {};
15227 for(var i = 0, len = d.length; i < len; i++){
15228 v = d[i].data[dataIndex];
15230 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15239 * Revert to a view of the Record cache with no filtering applied.
15240 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15242 clearFilter : function(suppressEvent){
15243 if(this.snapshot && this.snapshot != this.data){
15244 this.data = this.snapshot;
15245 delete this.snapshot;
15246 if(suppressEvent !== true){
15247 this.fireEvent("datachanged", this);
15253 afterEdit : function(record){
15254 if(this.modified.indexOf(record) == -1){
15255 this.modified.push(record);
15257 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15261 afterReject : function(record){
15262 this.modified.remove(record);
15263 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15267 afterCommit : function(record){
15268 this.modified.remove(record);
15269 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15273 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15274 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15276 commitChanges : function(){
15277 var m = this.modified.slice(0);
15278 this.modified = [];
15279 for(var i = 0, len = m.length; i < len; i++){
15285 * Cancel outstanding changes on all changed records.
15287 rejectChanges : function(){
15288 var m = this.modified.slice(0);
15289 this.modified = [];
15290 for(var i = 0, len = m.length; i < len; i++){
15295 onMetaChange : function(meta, rtype, o){
15296 this.recordType = rtype;
15297 this.fields = rtype.prototype.fields;
15298 delete this.snapshot;
15299 this.sortInfo = meta.sortInfo || this.sortInfo;
15300 this.modified = [];
15301 this.fireEvent('metachange', this, this.reader.meta);
15304 moveIndex : function(data, type)
15306 var index = this.indexOf(data);
15308 var newIndex = index + type;
15312 this.insert(newIndex, data);
15317 * Ext JS Library 1.1.1
15318 * Copyright(c) 2006-2007, Ext JS, LLC.
15320 * Originally Released Under LGPL - original licence link has changed is not relivant.
15323 * <script type="text/javascript">
15327 * @class Roo.data.SimpleStore
15328 * @extends Roo.data.Store
15329 * Small helper class to make creating Stores from Array data easier.
15330 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15331 * @cfg {Array} fields An array of field definition objects, or field name strings.
15332 * @cfg {Object} an existing reader (eg. copied from another store)
15333 * @cfg {Array} data The multi-dimensional array of data
15335 * @param {Object} config
15337 Roo.data.SimpleStore = function(config)
15339 Roo.data.SimpleStore.superclass.constructor.call(this, {
15341 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15344 Roo.data.Record.create(config.fields)
15346 proxy : new Roo.data.MemoryProxy(config.data)
15350 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15352 * Ext JS Library 1.1.1
15353 * Copyright(c) 2006-2007, Ext JS, LLC.
15355 * Originally Released Under LGPL - original licence link has changed is not relivant.
15358 * <script type="text/javascript">
15363 * @extends Roo.data.Store
15364 * @class Roo.data.JsonStore
15365 * Small helper class to make creating Stores for JSON data easier. <br/>
15367 var store = new Roo.data.JsonStore({
15368 url: 'get-images.php',
15370 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15373 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15374 * JsonReader and HttpProxy (unless inline data is provided).</b>
15375 * @cfg {Array} fields An array of field definition objects, or field name strings.
15377 * @param {Object} config
15379 Roo.data.JsonStore = function(c){
15380 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15381 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15382 reader: new Roo.data.JsonReader(c, c.fields)
15385 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15387 * Ext JS Library 1.1.1
15388 * Copyright(c) 2006-2007, Ext JS, LLC.
15390 * Originally Released Under LGPL - original licence link has changed is not relivant.
15393 * <script type="text/javascript">
15397 Roo.data.Field = function(config){
15398 if(typeof config == "string"){
15399 config = {name: config};
15401 Roo.apply(this, config);
15404 this.type = "auto";
15407 var st = Roo.data.SortTypes;
15408 // named sortTypes are supported, here we look them up
15409 if(typeof this.sortType == "string"){
15410 this.sortType = st[this.sortType];
15413 // set default sortType for strings and dates
15414 if(!this.sortType){
15417 this.sortType = st.asUCString;
15420 this.sortType = st.asDate;
15423 this.sortType = st.none;
15428 var stripRe = /[\$,%]/g;
15430 // prebuilt conversion function for this field, instead of
15431 // switching every time we're reading a value
15433 var cv, dateFormat = this.dateFormat;
15438 cv = function(v){ return v; };
15441 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15445 return v !== undefined && v !== null && v !== '' ?
15446 parseInt(String(v).replace(stripRe, ""), 10) : '';
15451 return v !== undefined && v !== null && v !== '' ?
15452 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15457 cv = function(v){ return v === true || v === "true" || v == 1; };
15464 if(v instanceof Date){
15468 if(dateFormat == "timestamp"){
15469 return new Date(v*1000);
15471 return Date.parseDate(v, dateFormat);
15473 var parsed = Date.parse(v);
15474 return parsed ? new Date(parsed) : null;
15483 Roo.data.Field.prototype = {
15491 * Ext JS Library 1.1.1
15492 * Copyright(c) 2006-2007, Ext JS, LLC.
15494 * Originally Released Under LGPL - original licence link has changed is not relivant.
15497 * <script type="text/javascript">
15500 // Base class for reading structured data from a data source. This class is intended to be
15501 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15504 * @class Roo.data.DataReader
15505 * Base class for reading structured data from a data source. This class is intended to be
15506 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15509 Roo.data.DataReader = function(meta, recordType){
15513 this.recordType = recordType instanceof Array ?
15514 Roo.data.Record.create(recordType) : recordType;
15517 Roo.data.DataReader.prototype = {
15520 readerType : 'Data',
15522 * Create an empty record
15523 * @param {Object} data (optional) - overlay some values
15524 * @return {Roo.data.Record} record created.
15526 newRow : function(d) {
15528 this.recordType.prototype.fields.each(function(c) {
15530 case 'int' : da[c.name] = 0; break;
15531 case 'date' : da[c.name] = new Date(); break;
15532 case 'float' : da[c.name] = 0.0; break;
15533 case 'boolean' : da[c.name] = false; break;
15534 default : da[c.name] = ""; break;
15538 return new this.recordType(Roo.apply(da, d));
15544 * Ext JS Library 1.1.1
15545 * Copyright(c) 2006-2007, Ext JS, LLC.
15547 * Originally Released Under LGPL - original licence link has changed is not relivant.
15550 * <script type="text/javascript">
15554 * @class Roo.data.DataProxy
15555 * @extends Roo.data.Observable
15556 * This class is an abstract base class for implementations which provide retrieval of
15557 * unformatted data objects.<br>
15559 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15560 * (of the appropriate type which knows how to parse the data object) to provide a block of
15561 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15563 * Custom implementations must implement the load method as described in
15564 * {@link Roo.data.HttpProxy#load}.
15566 Roo.data.DataProxy = function(){
15569 * @event beforeload
15570 * Fires before a network request is made to retrieve a data object.
15571 * @param {Object} This DataProxy object.
15572 * @param {Object} params The params parameter to the load function.
15577 * Fires before the load method's callback is called.
15578 * @param {Object} This DataProxy object.
15579 * @param {Object} o The data object.
15580 * @param {Object} arg The callback argument object passed to the load function.
15584 * @event loadexception
15585 * Fires if an Exception occurs during data retrieval.
15586 * @param {Object} This DataProxy object.
15587 * @param {Object} o The data object.
15588 * @param {Object} arg The callback argument object passed to the load function.
15589 * @param {Object} e The Exception.
15591 loadexception : true
15593 Roo.data.DataProxy.superclass.constructor.call(this);
15596 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15599 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15603 * Ext JS Library 1.1.1
15604 * Copyright(c) 2006-2007, Ext JS, LLC.
15606 * Originally Released Under LGPL - original licence link has changed is not relivant.
15609 * <script type="text/javascript">
15612 * @class Roo.data.MemoryProxy
15613 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15614 * to the Reader when its load method is called.
15616 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15618 Roo.data.MemoryProxy = function(data){
15622 Roo.data.MemoryProxy.superclass.constructor.call(this);
15626 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15629 * Load data from the requested source (in this case an in-memory
15630 * data object passed to the constructor), read the data object into
15631 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15632 * process that block using the passed callback.
15633 * @param {Object} params This parameter is not used by the MemoryProxy class.
15634 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15635 * object into a block of Roo.data.Records.
15636 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15637 * The function must be passed <ul>
15638 * <li>The Record block object</li>
15639 * <li>The "arg" argument from the load function</li>
15640 * <li>A boolean success indicator</li>
15642 * @param {Object} scope The scope in which to call the callback
15643 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15645 load : function(params, reader, callback, scope, arg){
15646 params = params || {};
15649 result = reader.readRecords(params.data ? params.data :this.data);
15651 this.fireEvent("loadexception", this, arg, null, e);
15652 callback.call(scope, null, arg, false);
15655 callback.call(scope, result, arg, true);
15659 update : function(params, records){
15664 * Ext JS Library 1.1.1
15665 * Copyright(c) 2006-2007, Ext JS, LLC.
15667 * Originally Released Under LGPL - original licence link has changed is not relivant.
15670 * <script type="text/javascript">
15673 * @class Roo.data.HttpProxy
15674 * @extends Roo.data.DataProxy
15675 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15676 * configured to reference a certain URL.<br><br>
15678 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15679 * from which the running page was served.<br><br>
15681 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15683 * Be aware that to enable the browser to parse an XML document, the server must set
15684 * the Content-Type header in the HTTP response to "text/xml".
15686 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15687 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15688 * will be used to make the request.
15690 Roo.data.HttpProxy = function(conn){
15691 Roo.data.HttpProxy.superclass.constructor.call(this);
15692 // is conn a conn config or a real conn?
15694 this.useAjax = !conn || !conn.events;
15698 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15699 // thse are take from connection...
15702 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15705 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15706 * extra parameters to each request made by this object. (defaults to undefined)
15709 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15710 * to each request made by this object. (defaults to undefined)
15713 * @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)
15716 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15719 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15725 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15729 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15730 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15731 * a finer-grained basis than the DataProxy events.
15733 getConnection : function(){
15734 return this.useAjax ? Roo.Ajax : this.conn;
15738 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15739 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15740 * process that block using the passed callback.
15741 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15742 * for the request to the remote server.
15743 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15744 * object into a block of Roo.data.Records.
15745 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15746 * The function must be passed <ul>
15747 * <li>The Record block object</li>
15748 * <li>The "arg" argument from the load function</li>
15749 * <li>A boolean success indicator</li>
15751 * @param {Object} scope The scope in which to call the callback
15752 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15754 load : function(params, reader, callback, scope, arg){
15755 if(this.fireEvent("beforeload", this, params) !== false){
15757 params : params || {},
15759 callback : callback,
15764 callback : this.loadResponse,
15768 Roo.applyIf(o, this.conn);
15769 if(this.activeRequest){
15770 Roo.Ajax.abort(this.activeRequest);
15772 this.activeRequest = Roo.Ajax.request(o);
15774 this.conn.request(o);
15777 callback.call(scope||this, null, arg, false);
15782 loadResponse : function(o, success, response){
15783 delete this.activeRequest;
15785 this.fireEvent("loadexception", this, o, response);
15786 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15791 result = o.reader.read(response);
15793 this.fireEvent("loadexception", this, o, response, e);
15794 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15798 this.fireEvent("load", this, o, o.request.arg);
15799 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15803 update : function(dataSet){
15808 updateResponse : function(dataSet){
15813 * Ext JS Library 1.1.1
15814 * Copyright(c) 2006-2007, Ext JS, LLC.
15816 * Originally Released Under LGPL - original licence link has changed is not relivant.
15819 * <script type="text/javascript">
15823 * @class Roo.data.ScriptTagProxy
15824 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15825 * other than the originating domain of the running page.<br><br>
15827 * <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
15828 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15830 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15831 * source code that is used as the source inside a <script> tag.<br><br>
15833 * In order for the browser to process the returned data, the server must wrap the data object
15834 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15835 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15836 * depending on whether the callback name was passed:
15839 boolean scriptTag = false;
15840 String cb = request.getParameter("callback");
15843 response.setContentType("text/javascript");
15845 response.setContentType("application/x-json");
15847 Writer out = response.getWriter();
15849 out.write(cb + "(");
15851 out.print(dataBlock.toJsonString());
15858 * @param {Object} config A configuration object.
15860 Roo.data.ScriptTagProxy = function(config){
15861 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15862 Roo.apply(this, config);
15863 this.head = document.getElementsByTagName("head")[0];
15866 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15868 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15870 * @cfg {String} url The URL from which to request the data object.
15873 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15877 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15878 * the server the name of the callback function set up by the load call to process the returned data object.
15879 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15880 * javascript output which calls this named function passing the data object as its only parameter.
15882 callbackParam : "callback",
15884 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15885 * name to the request.
15890 * Load data from the configured URL, read the data object into
15891 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15892 * process that block using the passed callback.
15893 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15894 * for the request to the remote server.
15895 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15896 * object into a block of Roo.data.Records.
15897 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15898 * The function must be passed <ul>
15899 * <li>The Record block object</li>
15900 * <li>The "arg" argument from the load function</li>
15901 * <li>A boolean success indicator</li>
15903 * @param {Object} scope The scope in which to call the callback
15904 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15906 load : function(params, reader, callback, scope, arg){
15907 if(this.fireEvent("beforeload", this, params) !== false){
15909 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15911 var url = this.url;
15912 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15914 url += "&_dc=" + (new Date().getTime());
15916 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15919 cb : "stcCallback"+transId,
15920 scriptId : "stcScript"+transId,
15924 callback : callback,
15930 window[trans.cb] = function(o){
15931 conn.handleResponse(o, trans);
15934 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15936 if(this.autoAbort !== false){
15940 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15942 var script = document.createElement("script");
15943 script.setAttribute("src", url);
15944 script.setAttribute("type", "text/javascript");
15945 script.setAttribute("id", trans.scriptId);
15946 this.head.appendChild(script);
15948 this.trans = trans;
15950 callback.call(scope||this, null, arg, false);
15955 isLoading : function(){
15956 return this.trans ? true : false;
15960 * Abort the current server request.
15962 abort : function(){
15963 if(this.isLoading()){
15964 this.destroyTrans(this.trans);
15969 destroyTrans : function(trans, isLoaded){
15970 this.head.removeChild(document.getElementById(trans.scriptId));
15971 clearTimeout(trans.timeoutId);
15973 window[trans.cb] = undefined;
15975 delete window[trans.cb];
15978 // if hasn't been loaded, wait for load to remove it to prevent script error
15979 window[trans.cb] = function(){
15980 window[trans.cb] = undefined;
15982 delete window[trans.cb];
15989 handleResponse : function(o, trans){
15990 this.trans = false;
15991 this.destroyTrans(trans, true);
15994 result = trans.reader.readRecords(o);
15996 this.fireEvent("loadexception", this, o, trans.arg, e);
15997 trans.callback.call(trans.scope||window, null, trans.arg, false);
16000 this.fireEvent("load", this, o, trans.arg);
16001 trans.callback.call(trans.scope||window, result, trans.arg, true);
16005 handleFailure : function(trans){
16006 this.trans = false;
16007 this.destroyTrans(trans, false);
16008 this.fireEvent("loadexception", this, null, trans.arg);
16009 trans.callback.call(trans.scope||window, null, trans.arg, false);
16013 * Ext JS Library 1.1.1
16014 * Copyright(c) 2006-2007, Ext JS, LLC.
16016 * Originally Released Under LGPL - original licence link has changed is not relivant.
16019 * <script type="text/javascript">
16023 * @class Roo.data.JsonReader
16024 * @extends Roo.data.DataReader
16025 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16026 * based on mappings in a provided Roo.data.Record constructor.
16028 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16029 * in the reply previously.
16034 var RecordDef = Roo.data.Record.create([
16035 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16036 {name: 'occupation'} // This field will use "occupation" as the mapping.
16038 var myReader = new Roo.data.JsonReader({
16039 totalProperty: "results", // The property which contains the total dataset size (optional)
16040 root: "rows", // The property which contains an Array of row objects
16041 id: "id" // The property within each row object that provides an ID for the record (optional)
16045 * This would consume a JSON file like this:
16047 { 'results': 2, 'rows': [
16048 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16049 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16052 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16053 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16054 * paged from the remote server.
16055 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16056 * @cfg {String} root name of the property which contains the Array of row objects.
16057 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16058 * @cfg {Array} fields Array of field definition objects
16060 * Create a new JsonReader
16061 * @param {Object} meta Metadata configuration options
16062 * @param {Object} recordType Either an Array of field definition objects,
16063 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16065 Roo.data.JsonReader = function(meta, recordType){
16068 // set some defaults:
16069 Roo.applyIf(meta, {
16070 totalProperty: 'total',
16071 successProperty : 'success',
16076 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16078 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16080 readerType : 'Json',
16083 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16084 * Used by Store query builder to append _requestMeta to params.
16087 metaFromRemote : false,
16089 * This method is only used by a DataProxy which has retrieved data from a remote server.
16090 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16091 * @return {Object} data A data block which is used by an Roo.data.Store object as
16092 * a cache of Roo.data.Records.
16094 read : function(response){
16095 var json = response.responseText;
16097 var o = /* eval:var:o */ eval("("+json+")");
16099 throw {message: "JsonReader.read: Json object not found"};
16105 this.metaFromRemote = true;
16106 this.meta = o.metaData;
16107 this.recordType = Roo.data.Record.create(o.metaData.fields);
16108 this.onMetaChange(this.meta, this.recordType, o);
16110 return this.readRecords(o);
16113 // private function a store will implement
16114 onMetaChange : function(meta, recordType, o){
16121 simpleAccess: function(obj, subsc) {
16128 getJsonAccessor: function(){
16130 return function(expr) {
16132 return(re.test(expr))
16133 ? new Function("obj", "return obj." + expr)
16138 return Roo.emptyFn;
16143 * Create a data block containing Roo.data.Records from an XML document.
16144 * @param {Object} o An object which contains an Array of row objects in the property specified
16145 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16146 * which contains the total size of the dataset.
16147 * @return {Object} data A data block which is used by an Roo.data.Store object as
16148 * a cache of Roo.data.Records.
16150 readRecords : function(o){
16152 * After any data loads, the raw JSON data is available for further custom processing.
16156 var s = this.meta, Record = this.recordType,
16157 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16159 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16161 if(s.totalProperty) {
16162 this.getTotal = this.getJsonAccessor(s.totalProperty);
16164 if(s.successProperty) {
16165 this.getSuccess = this.getJsonAccessor(s.successProperty);
16167 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16169 var g = this.getJsonAccessor(s.id);
16170 this.getId = function(rec) {
16172 return (r === undefined || r === "") ? null : r;
16175 this.getId = function(){return null;};
16178 for(var jj = 0; jj < fl; jj++){
16180 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16181 this.ef[jj] = this.getJsonAccessor(map);
16185 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16186 if(s.totalProperty){
16187 var vt = parseInt(this.getTotal(o), 10);
16192 if(s.successProperty){
16193 var vs = this.getSuccess(o);
16194 if(vs === false || vs === 'false'){
16199 for(var i = 0; i < c; i++){
16202 var id = this.getId(n);
16203 for(var j = 0; j < fl; j++){
16205 var v = this.ef[j](n);
16207 Roo.log('missing convert for ' + f.name);
16211 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16213 var record = new Record(values, id);
16215 records[i] = record;
16221 totalRecords : totalRecords
16224 // used when loading children.. @see loadDataFromChildren
16225 toLoadData: function(rec)
16227 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16228 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16229 return { data : data, total : data.length };
16234 * Ext JS Library 1.1.1
16235 * Copyright(c) 2006-2007, Ext JS, LLC.
16237 * Originally Released Under LGPL - original licence link has changed is not relivant.
16240 * <script type="text/javascript">
16244 * @class Roo.data.ArrayReader
16245 * @extends Roo.data.DataReader
16246 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16247 * Each element of that Array represents a row of data fields. The
16248 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16249 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16253 var RecordDef = Roo.data.Record.create([
16254 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16255 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16257 var myReader = new Roo.data.ArrayReader({
16258 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16262 * This would consume an Array like this:
16264 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16268 * Create a new JsonReader
16269 * @param {Object} meta Metadata configuration options.
16270 * @param {Object|Array} recordType Either an Array of field definition objects
16272 * @cfg {Array} fields Array of field definition objects
16273 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16274 * as specified to {@link Roo.data.Record#create},
16275 * or an {@link Roo.data.Record} object
16278 * created using {@link Roo.data.Record#create}.
16280 Roo.data.ArrayReader = function(meta, recordType)
16282 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16285 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16288 * Create a data block containing Roo.data.Records from an XML document.
16289 * @param {Object} o An Array of row objects which represents the dataset.
16290 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16291 * a cache of Roo.data.Records.
16293 readRecords : function(o)
16295 var sid = this.meta ? this.meta.id : null;
16296 var recordType = this.recordType, fields = recordType.prototype.fields;
16299 for(var i = 0; i < root.length; i++){
16302 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16303 for(var j = 0, jlen = fields.length; j < jlen; j++){
16304 var f = fields.items[j];
16305 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16306 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16308 values[f.name] = v;
16310 var record = new recordType(values, id);
16312 records[records.length] = record;
16316 totalRecords : records.length
16319 // used when loading children.. @see loadDataFromChildren
16320 toLoadData: function(rec)
16322 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16323 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16334 * @class Roo.bootstrap.ComboBox
16335 * @extends Roo.bootstrap.TriggerField
16336 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16337 * @cfg {Boolean} append (true|false) default false
16338 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16339 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16340 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16341 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16342 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16343 * @cfg {Boolean} animate default true
16344 * @cfg {Boolean} emptyResultText only for touch device
16345 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16346 * @cfg {String} emptyTitle default ''
16347 * @cfg {Number} width fixed with? experimental
16349 * Create a new ComboBox.
16350 * @param {Object} config Configuration options
16352 Roo.bootstrap.ComboBox = function(config){
16353 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16357 * Fires when the dropdown list is expanded
16358 * @param {Roo.bootstrap.ComboBox} combo This combo box
16363 * Fires when the dropdown list is collapsed
16364 * @param {Roo.bootstrap.ComboBox} combo This combo box
16368 * @event beforeselect
16369 * Fires before a list item is selected. Return false to cancel the selection.
16370 * @param {Roo.bootstrap.ComboBox} combo This combo box
16371 * @param {Roo.data.Record} record The data record returned from the underlying store
16372 * @param {Number} index The index of the selected item in the dropdown list
16374 'beforeselect' : true,
16377 * Fires when a list item is selected
16378 * @param {Roo.bootstrap.ComboBox} combo This combo box
16379 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16380 * @param {Number} index The index of the selected item in the dropdown list
16384 * @event beforequery
16385 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16386 * The event object passed has these properties:
16387 * @param {Roo.bootstrap.ComboBox} combo This combo box
16388 * @param {String} query The query
16389 * @param {Boolean} forceAll true to force "all" query
16390 * @param {Boolean} cancel true to cancel the query
16391 * @param {Object} e The query event object
16393 'beforequery': true,
16396 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16397 * @param {Roo.bootstrap.ComboBox} combo This combo box
16402 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16403 * @param {Roo.bootstrap.ComboBox} combo This combo box
16404 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16409 * Fires when the remove value from the combobox array
16410 * @param {Roo.bootstrap.ComboBox} combo This combo box
16414 * @event afterremove
16415 * Fires when the remove value from the combobox array
16416 * @param {Roo.bootstrap.ComboBox} combo This combo box
16418 'afterremove' : true,
16420 * @event specialfilter
16421 * Fires when specialfilter
16422 * @param {Roo.bootstrap.ComboBox} combo This combo box
16424 'specialfilter' : true,
16427 * Fires when tick the element
16428 * @param {Roo.bootstrap.ComboBox} combo This combo box
16432 * @event touchviewdisplay
16433 * Fires when touch view require special display (default is using displayField)
16434 * @param {Roo.bootstrap.ComboBox} combo This combo box
16435 * @param {Object} cfg set html .
16437 'touchviewdisplay' : true
16442 this.tickItems = [];
16444 this.selectedIndex = -1;
16445 if(this.mode == 'local'){
16446 if(config.queryDelay === undefined){
16447 this.queryDelay = 10;
16449 if(config.minChars === undefined){
16455 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16458 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16459 * rendering into an Roo.Editor, defaults to false)
16462 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16463 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16466 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16469 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16470 * the dropdown list (defaults to undefined, with no header element)
16474 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16478 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16480 listWidth: undefined,
16482 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16483 * mode = 'remote' or 'text' if mode = 'local')
16485 displayField: undefined,
16488 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16489 * mode = 'remote' or 'value' if mode = 'local').
16490 * Note: use of a valueField requires the user make a selection
16491 * in order for a value to be mapped.
16493 valueField: undefined,
16495 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16500 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16501 * field's data value (defaults to the underlying DOM element's name)
16503 hiddenName: undefined,
16505 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16509 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16511 selectedClass: 'active',
16514 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16518 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16519 * anchor positions (defaults to 'tl-bl')
16521 listAlign: 'tl-bl?',
16523 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16527 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16528 * query specified by the allQuery config option (defaults to 'query')
16530 triggerAction: 'query',
16532 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16533 * (defaults to 4, does not apply if editable = false)
16537 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16538 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16542 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16543 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16547 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16548 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16552 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16553 * when editable = true (defaults to false)
16555 selectOnFocus:false,
16557 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16559 queryParam: 'query',
16561 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16562 * when mode = 'remote' (defaults to 'Loading...')
16564 loadingText: 'Loading...',
16566 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16570 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16574 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16575 * traditional select (defaults to true)
16579 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16583 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16587 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16588 * listWidth has a higher value)
16592 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16593 * allow the user to set arbitrary text into the field (defaults to false)
16595 forceSelection:false,
16597 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16598 * if typeAhead = true (defaults to 250)
16600 typeAheadDelay : 250,
16602 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16603 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16605 valueNotFoundText : undefined,
16607 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16609 blockFocus : false,
16612 * @cfg {Boolean} disableClear Disable showing of clear button.
16614 disableClear : false,
16616 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16618 alwaysQuery : false,
16621 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16626 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16628 invalidClass : "has-warning",
16631 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16633 validClass : "has-success",
16636 * @cfg {Boolean} specialFilter (true|false) special filter default false
16638 specialFilter : false,
16641 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16643 mobileTouchView : true,
16646 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16648 useNativeIOS : false,
16651 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16653 mobile_restrict_height : false,
16655 ios_options : false,
16667 btnPosition : 'right',
16668 triggerList : true,
16669 showToggleBtn : true,
16671 emptyResultText: 'Empty',
16672 triggerText : 'Select',
16676 // element that contains real text value.. (when hidden is used..)
16678 getAutoCreate : function()
16683 * Render classic select for iso
16686 if(Roo.isIOS && this.useNativeIOS){
16687 cfg = this.getAutoCreateNativeIOS();
16695 if(Roo.isTouch && this.mobileTouchView){
16696 cfg = this.getAutoCreateTouchView();
16703 if(!this.tickable){
16704 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16709 * ComboBox with tickable selections
16712 var align = this.labelAlign || this.parentLabelAlign();
16715 cls : 'form-group roo-combobox-tickable' //input-group
16718 var btn_text_select = '';
16719 var btn_text_done = '';
16720 var btn_text_cancel = '';
16722 if (this.btn_text_show) {
16723 btn_text_select = 'Select';
16724 btn_text_done = 'Done';
16725 btn_text_cancel = 'Cancel';
16730 cls : 'tickable-buttons',
16735 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16736 //html : this.triggerText
16737 html: btn_text_select
16743 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16745 html: btn_text_done
16751 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16753 html: btn_text_cancel
16759 buttons.cn.unshift({
16761 cls: 'roo-select2-search-field-input'
16767 Roo.each(buttons.cn, function(c){
16769 c.cls += ' btn-' + _this.size;
16772 if (_this.disabled) {
16779 style : 'display: contents',
16784 cls: 'form-hidden-field'
16788 cls: 'roo-select2-choices',
16792 cls: 'roo-select2-search-field',
16803 cls: 'roo-select2-container input-group roo-select2-container-multi',
16809 // cls: 'typeahead typeahead-long dropdown-menu',
16810 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16815 if(this.hasFeedback && !this.allowBlank){
16819 cls: 'glyphicon form-control-feedback'
16822 combobox.cn.push(feedback);
16829 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16830 tooltip : 'This field is required'
16832 if (Roo.bootstrap.version == 4) {
16835 style : 'display:none'
16838 if (align ==='left' && this.fieldLabel.length) {
16840 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16847 cls : 'control-label col-form-label',
16848 html : this.fieldLabel
16860 var labelCfg = cfg.cn[1];
16861 var contentCfg = cfg.cn[2];
16864 if(this.indicatorpos == 'right'){
16870 cls : 'control-label col-form-label',
16874 html : this.fieldLabel
16890 labelCfg = cfg.cn[0];
16891 contentCfg = cfg.cn[1];
16895 if(this.labelWidth > 12){
16896 labelCfg.style = "width: " + this.labelWidth + 'px';
16898 if(this.width * 1 > 0){
16899 contentCfg.style = "width: " + this.width + 'px';
16901 if(this.labelWidth < 13 && this.labelmd == 0){
16902 this.labelmd = this.labelWidth;
16905 if(this.labellg > 0){
16906 labelCfg.cls += ' col-lg-' + this.labellg;
16907 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16910 if(this.labelmd > 0){
16911 labelCfg.cls += ' col-md-' + this.labelmd;
16912 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16915 if(this.labelsm > 0){
16916 labelCfg.cls += ' col-sm-' + this.labelsm;
16917 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16920 if(this.labelxs > 0){
16921 labelCfg.cls += ' col-xs-' + this.labelxs;
16922 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16926 } else if ( this.fieldLabel.length) {
16927 // Roo.log(" label");
16932 //cls : 'input-group-addon',
16933 html : this.fieldLabel
16938 if(this.indicatorpos == 'right'){
16942 //cls : 'input-group-addon',
16943 html : this.fieldLabel
16953 // Roo.log(" no label && no align");
16960 ['xs','sm','md','lg'].map(function(size){
16961 if (settings[size]) {
16962 cfg.cls += ' col-' + size + '-' + settings[size];
16970 _initEventsCalled : false,
16973 initEvents: function()
16975 if (this._initEventsCalled) { // as we call render... prevent looping...
16978 this._initEventsCalled = true;
16981 throw "can not find store for combo";
16984 this.indicator = this.indicatorEl();
16986 this.store = Roo.factory(this.store, Roo.data);
16987 this.store.parent = this;
16989 // if we are building from html. then this element is so complex, that we can not really
16990 // use the rendered HTML.
16991 // so we have to trash and replace the previous code.
16992 if (Roo.XComponent.build_from_html) {
16993 // remove this element....
16994 var e = this.el.dom, k=0;
16995 while (e ) { e = e.previousSibling; ++k;}
17000 this.rendered = false;
17002 this.render(this.parent().getChildContainer(true), k);
17005 if(Roo.isIOS && this.useNativeIOS){
17006 this.initIOSView();
17014 if(Roo.isTouch && this.mobileTouchView){
17015 this.initTouchView();
17020 this.initTickableEvents();
17024 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17026 if(this.hiddenName){
17028 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17030 this.hiddenField.dom.value =
17031 this.hiddenValue !== undefined ? this.hiddenValue :
17032 this.value !== undefined ? this.value : '';
17034 // prevent input submission
17035 this.el.dom.removeAttribute('name');
17036 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17041 // this.el.dom.setAttribute('autocomplete', 'off');
17044 var cls = 'x-combo-list';
17046 //this.list = new Roo.Layer({
17047 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17053 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17054 _this.list.setWidth(lw);
17057 this.list.on('mouseover', this.onViewOver, this);
17058 this.list.on('mousemove', this.onViewMove, this);
17059 this.list.on('scroll', this.onViewScroll, this);
17062 this.list.swallowEvent('mousewheel');
17063 this.assetHeight = 0;
17066 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17067 this.assetHeight += this.header.getHeight();
17070 this.innerList = this.list.createChild({cls:cls+'-inner'});
17071 this.innerList.on('mouseover', this.onViewOver, this);
17072 this.innerList.on('mousemove', this.onViewMove, this);
17073 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17075 if(this.allowBlank && !this.pageSize && !this.disableClear){
17076 this.footer = this.list.createChild({cls:cls+'-ft'});
17077 this.pageTb = new Roo.Toolbar(this.footer);
17081 this.footer = this.list.createChild({cls:cls+'-ft'});
17082 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17083 {pageSize: this.pageSize});
17087 if (this.pageTb && this.allowBlank && !this.disableClear) {
17089 this.pageTb.add(new Roo.Toolbar.Fill(), {
17090 cls: 'x-btn-icon x-btn-clear',
17092 handler: function()
17095 _this.clearValue();
17096 _this.onSelect(false, -1);
17101 this.assetHeight += this.footer.getHeight();
17106 this.tpl = Roo.bootstrap.version == 4 ?
17107 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17108 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17111 this.view = new Roo.View(this.list, this.tpl, {
17112 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17114 //this.view.wrapEl.setDisplayed(false);
17115 this.view.on('click', this.onViewClick, this);
17118 this.store.on('beforeload', this.onBeforeLoad, this);
17119 this.store.on('load', this.onLoad, this);
17120 this.store.on('loadexception', this.onLoadException, this);
17122 if(this.resizable){
17123 this.resizer = new Roo.Resizable(this.list, {
17124 pinned:true, handles:'se'
17126 this.resizer.on('resize', function(r, w, h){
17127 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17128 this.listWidth = w;
17129 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17130 this.restrictHeight();
17132 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17135 if(!this.editable){
17136 this.editable = true;
17137 this.setEditable(false);
17142 if (typeof(this.events.add.listeners) != 'undefined') {
17144 this.addicon = this.wrap.createChild(
17145 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17147 this.addicon.on('click', function(e) {
17148 this.fireEvent('add', this);
17151 if (typeof(this.events.edit.listeners) != 'undefined') {
17153 this.editicon = this.wrap.createChild(
17154 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17155 if (this.addicon) {
17156 this.editicon.setStyle('margin-left', '40px');
17158 this.editicon.on('click', function(e) {
17160 // we fire even if inothing is selected..
17161 this.fireEvent('edit', this, this.lastData );
17167 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17168 "up" : function(e){
17169 this.inKeyMode = true;
17173 "down" : function(e){
17174 if(!this.isExpanded()){
17175 this.onTriggerClick();
17177 this.inKeyMode = true;
17182 "enter" : function(e){
17183 // this.onViewClick();
17187 if(this.fireEvent("specialkey", this, e)){
17188 this.onViewClick(false);
17194 "esc" : function(e){
17198 "tab" : function(e){
17201 if(this.fireEvent("specialkey", this, e)){
17202 this.onViewClick(false);
17210 doRelay : function(foo, bar, hname){
17211 if(hname == 'down' || this.scope.isExpanded()){
17212 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17221 this.queryDelay = Math.max(this.queryDelay || 10,
17222 this.mode == 'local' ? 10 : 250);
17225 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17227 if(this.typeAhead){
17228 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17230 if(this.editable !== false){
17231 this.inputEl().on("keyup", this.onKeyUp, this);
17233 if(this.forceSelection){
17234 this.inputEl().on('blur', this.doForce, this);
17238 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17239 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17243 initTickableEvents: function()
17247 if(this.hiddenName){
17249 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17251 this.hiddenField.dom.value =
17252 this.hiddenValue !== undefined ? this.hiddenValue :
17253 this.value !== undefined ? this.value : '';
17255 // prevent input submission
17256 this.el.dom.removeAttribute('name');
17257 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17262 // this.list = this.el.select('ul.dropdown-menu',true).first();
17264 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17265 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17266 if(this.triggerList){
17267 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17270 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17271 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17273 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17274 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17276 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17277 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17279 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17280 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17281 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17284 this.cancelBtn.hide();
17289 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17290 _this.list.setWidth(lw);
17293 this.list.on('mouseover', this.onViewOver, this);
17294 this.list.on('mousemove', this.onViewMove, this);
17296 this.list.on('scroll', this.onViewScroll, this);
17299 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17300 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17303 this.view = new Roo.View(this.list, this.tpl, {
17308 selectedClass: this.selectedClass
17311 //this.view.wrapEl.setDisplayed(false);
17312 this.view.on('click', this.onViewClick, this);
17316 this.store.on('beforeload', this.onBeforeLoad, this);
17317 this.store.on('load', this.onLoad, this);
17318 this.store.on('loadexception', this.onLoadException, this);
17321 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17322 "up" : function(e){
17323 this.inKeyMode = true;
17327 "down" : function(e){
17328 this.inKeyMode = true;
17332 "enter" : function(e){
17333 if(this.fireEvent("specialkey", this, e)){
17334 this.onViewClick(false);
17340 "esc" : function(e){
17341 this.onTickableFooterButtonClick(e, false, false);
17344 "tab" : function(e){
17345 this.fireEvent("specialkey", this, e);
17347 this.onTickableFooterButtonClick(e, false, false);
17354 doRelay : function(e, fn, key){
17355 if(this.scope.isExpanded()){
17356 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17365 this.queryDelay = Math.max(this.queryDelay || 10,
17366 this.mode == 'local' ? 10 : 250);
17369 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17371 if(this.typeAhead){
17372 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17375 if(this.editable !== false){
17376 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17379 this.indicator = this.indicatorEl();
17381 if(this.indicator){
17382 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17383 this.indicator.hide();
17388 onDestroy : function(){
17390 this.view.setStore(null);
17391 this.view.el.removeAllListeners();
17392 this.view.el.remove();
17393 this.view.purgeListeners();
17396 this.list.dom.innerHTML = '';
17400 this.store.un('beforeload', this.onBeforeLoad, this);
17401 this.store.un('load', this.onLoad, this);
17402 this.store.un('loadexception', this.onLoadException, this);
17404 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17408 fireKey : function(e){
17409 if(e.isNavKeyPress() && !this.list.isVisible()){
17410 this.fireEvent("specialkey", this, e);
17415 onResize: function(w, h)
17419 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17421 // if(typeof w != 'number'){
17422 // // we do not handle it!?!?
17425 // var tw = this.trigger.getWidth();
17426 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17427 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17429 // this.inputEl().setWidth( this.adjustWidth('input', x));
17431 // //this.trigger.setStyle('left', x+'px');
17433 // if(this.list && this.listWidth === undefined){
17434 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17435 // this.list.setWidth(lw);
17436 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17444 * Allow or prevent the user from directly editing the field text. If false is passed,
17445 * the user will only be able to select from the items defined in the dropdown list. This method
17446 * is the runtime equivalent of setting the 'editable' config option at config time.
17447 * @param {Boolean} value True to allow the user to directly edit the field text
17449 setEditable : function(value){
17450 if(value == this.editable){
17453 this.editable = value;
17455 this.inputEl().dom.setAttribute('readOnly', true);
17456 this.inputEl().on('mousedown', this.onTriggerClick, this);
17457 this.inputEl().addClass('x-combo-noedit');
17459 this.inputEl().dom.removeAttribute('readOnly');
17460 this.inputEl().un('mousedown', this.onTriggerClick, this);
17461 this.inputEl().removeClass('x-combo-noedit');
17467 onBeforeLoad : function(combo,opts){
17468 if(!this.hasFocus){
17472 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17474 this.restrictHeight();
17475 this.selectedIndex = -1;
17479 onLoad : function(){
17481 this.hasQuery = false;
17483 if(!this.hasFocus){
17487 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17488 this.loading.hide();
17491 if(this.store.getCount() > 0){
17494 this.restrictHeight();
17495 if(this.lastQuery == this.allQuery){
17496 if(this.editable && !this.tickable){
17497 this.inputEl().dom.select();
17501 !this.selectByValue(this.value, true) &&
17504 !this.store.lastOptions ||
17505 typeof(this.store.lastOptions.add) == 'undefined' ||
17506 this.store.lastOptions.add != true
17509 this.select(0, true);
17512 if(this.autoFocus){
17515 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17516 this.taTask.delay(this.typeAheadDelay);
17520 this.onEmptyResults();
17526 onLoadException : function()
17528 this.hasQuery = false;
17530 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17531 this.loading.hide();
17534 if(this.tickable && this.editable){
17539 // only causes errors at present
17540 //Roo.log(this.store.reader.jsonData);
17541 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17543 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17549 onTypeAhead : function(){
17550 if(this.store.getCount() > 0){
17551 var r = this.store.getAt(0);
17552 var newValue = r.data[this.displayField];
17553 var len = newValue.length;
17554 var selStart = this.getRawValue().length;
17556 if(selStart != len){
17557 this.setRawValue(newValue);
17558 this.selectText(selStart, newValue.length);
17564 onSelect : function(record, index){
17566 if(this.fireEvent('beforeselect', this, record, index) !== false){
17568 this.setFromData(index > -1 ? record.data : false);
17571 this.fireEvent('select', this, record, index);
17576 * Returns the currently selected field value or empty string if no value is set.
17577 * @return {String} value The selected value
17579 getValue : function()
17581 if(Roo.isIOS && this.useNativeIOS){
17582 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17586 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17589 if(this.valueField){
17590 return typeof this.value != 'undefined' ? this.value : '';
17592 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17596 getRawValue : function()
17598 if(Roo.isIOS && this.useNativeIOS){
17599 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17602 var v = this.inputEl().getValue();
17608 * Clears any text/value currently set in the field
17610 clearValue : function(){
17612 if(this.hiddenField){
17613 this.hiddenField.dom.value = '';
17616 this.setRawValue('');
17617 this.lastSelectionText = '';
17618 this.lastData = false;
17620 var close = this.closeTriggerEl();
17631 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17632 * will be displayed in the field. If the value does not match the data value of an existing item,
17633 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17634 * Otherwise the field will be blank (although the value will still be set).
17635 * @param {String} value The value to match
17637 setValue : function(v)
17639 if(Roo.isIOS && this.useNativeIOS){
17640 this.setIOSValue(v);
17650 if(this.valueField){
17651 var r = this.findRecord(this.valueField, v);
17653 text = r.data[this.displayField];
17654 }else if(this.valueNotFoundText !== undefined){
17655 text = this.valueNotFoundText;
17658 this.lastSelectionText = text;
17659 if(this.hiddenField){
17660 this.hiddenField.dom.value = v;
17662 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17665 var close = this.closeTriggerEl();
17668 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17674 * @property {Object} the last set data for the element
17679 * Sets the value of the field based on a object which is related to the record format for the store.
17680 * @param {Object} value the value to set as. or false on reset?
17682 setFromData : function(o){
17689 var dv = ''; // display value
17690 var vv = ''; // value value..
17692 if (this.displayField) {
17693 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17695 // this is an error condition!!!
17696 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17699 if(this.valueField){
17700 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17703 var close = this.closeTriggerEl();
17706 if(dv.length || vv * 1 > 0){
17708 this.blockFocus=true;
17714 if(this.hiddenField){
17715 this.hiddenField.dom.value = vv;
17717 this.lastSelectionText = dv;
17718 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17722 // no hidden field.. - we store the value in 'value', but still display
17723 // display field!!!!
17724 this.lastSelectionText = dv;
17725 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17732 reset : function(){
17733 // overridden so that last data is reset..
17740 this.setValue(this.originalValue);
17741 //this.clearInvalid();
17742 this.lastData = false;
17744 this.view.clearSelections();
17750 findRecord : function(prop, value){
17752 if(this.store.getCount() > 0){
17753 this.store.each(function(r){
17754 if(r.data[prop] == value){
17764 getName: function()
17766 // returns hidden if it's set..
17767 if (!this.rendered) {return ''};
17768 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17772 onViewMove : function(e, t){
17773 this.inKeyMode = false;
17777 onViewOver : function(e, t){
17778 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17781 var item = this.view.findItemFromChild(t);
17784 var index = this.view.indexOf(item);
17785 this.select(index, false);
17790 onViewClick : function(view, doFocus, el, e)
17792 var index = this.view.getSelectedIndexes()[0];
17794 var r = this.store.getAt(index);
17798 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17805 Roo.each(this.tickItems, function(v,k){
17807 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17809 _this.tickItems.splice(k, 1);
17811 if(typeof(e) == 'undefined' && view == false){
17812 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17824 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17825 this.tickItems.push(r.data);
17828 if(typeof(e) == 'undefined' && view == false){
17829 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17836 this.onSelect(r, index);
17838 if(doFocus !== false && !this.blockFocus){
17839 this.inputEl().focus();
17844 restrictHeight : function(){
17845 //this.innerList.dom.style.height = '';
17846 //var inner = this.innerList.dom;
17847 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17848 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17849 //this.list.beginUpdate();
17850 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17851 this.list.alignTo(this.inputEl(), this.listAlign);
17852 this.list.alignTo(this.inputEl(), this.listAlign);
17853 //this.list.endUpdate();
17857 onEmptyResults : function(){
17859 if(this.tickable && this.editable){
17860 this.hasFocus = false;
17861 this.restrictHeight();
17869 * Returns true if the dropdown list is expanded, else false.
17871 isExpanded : function(){
17872 return this.list.isVisible();
17876 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17877 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17878 * @param {String} value The data value of the item to select
17879 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17880 * selected item if it is not currently in view (defaults to true)
17881 * @return {Boolean} True if the value matched an item in the list, else false
17883 selectByValue : function(v, scrollIntoView){
17884 if(v !== undefined && v !== null){
17885 var r = this.findRecord(this.valueField || this.displayField, v);
17887 this.select(this.store.indexOf(r), scrollIntoView);
17895 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17896 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17897 * @param {Number} index The zero-based index of the list item to select
17898 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17899 * selected item if it is not currently in view (defaults to true)
17901 select : function(index, scrollIntoView){
17902 this.selectedIndex = index;
17903 this.view.select(index);
17904 if(scrollIntoView !== false){
17905 var el = this.view.getNode(index);
17907 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17910 this.list.scrollChildIntoView(el, false);
17916 selectNext : function(){
17917 var ct = this.store.getCount();
17919 if(this.selectedIndex == -1){
17921 }else if(this.selectedIndex < ct-1){
17922 this.select(this.selectedIndex+1);
17928 selectPrev : function(){
17929 var ct = this.store.getCount();
17931 if(this.selectedIndex == -1){
17933 }else if(this.selectedIndex != 0){
17934 this.select(this.selectedIndex-1);
17940 onKeyUp : function(e){
17941 if(this.editable !== false && !e.isSpecialKey()){
17942 this.lastKey = e.getKey();
17943 this.dqTask.delay(this.queryDelay);
17948 validateBlur : function(){
17949 return !this.list || !this.list.isVisible();
17953 initQuery : function(){
17955 var v = this.getRawValue();
17957 if(this.tickable && this.editable){
17958 v = this.tickableInputEl().getValue();
17965 doForce : function(){
17966 if(this.inputEl().dom.value.length > 0){
17967 this.inputEl().dom.value =
17968 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17974 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17975 * query allowing the query action to be canceled if needed.
17976 * @param {String} query The SQL query to execute
17977 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17978 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17979 * saved in the current store (defaults to false)
17981 doQuery : function(q, forceAll){
17983 if(q === undefined || q === null){
17988 forceAll: forceAll,
17992 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17997 forceAll = qe.forceAll;
17998 if(forceAll === true || (q.length >= this.minChars)){
18000 this.hasQuery = true;
18002 if(this.lastQuery != q || this.alwaysQuery){
18003 this.lastQuery = q;
18004 if(this.mode == 'local'){
18005 this.selectedIndex = -1;
18007 this.store.clearFilter();
18010 if(this.specialFilter){
18011 this.fireEvent('specialfilter', this);
18016 this.store.filter(this.displayField, q);
18019 this.store.fireEvent("datachanged", this.store);
18026 this.store.baseParams[this.queryParam] = q;
18028 var options = {params : this.getParams(q)};
18031 options.add = true;
18032 options.params.start = this.page * this.pageSize;
18035 this.store.load(options);
18038 * this code will make the page width larger, at the beginning, the list not align correctly,
18039 * we should expand the list on onLoad
18040 * so command out it
18045 this.selectedIndex = -1;
18050 this.loadNext = false;
18054 getParams : function(q){
18056 //p[this.queryParam] = q;
18060 p.limit = this.pageSize;
18066 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18068 collapse : function(){
18069 if(!this.isExpanded()){
18075 this.hasFocus = false;
18079 this.cancelBtn.hide();
18080 this.trigger.show();
18083 this.tickableInputEl().dom.value = '';
18084 this.tickableInputEl().blur();
18089 Roo.get(document).un('mousedown', this.collapseIf, this);
18090 Roo.get(document).un('mousewheel', this.collapseIf, this);
18091 if (!this.editable) {
18092 Roo.get(document).un('keydown', this.listKeyPress, this);
18094 this.fireEvent('collapse', this);
18100 collapseIf : function(e){
18101 var in_combo = e.within(this.el);
18102 var in_list = e.within(this.list);
18103 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18105 if (in_combo || in_list || is_list) {
18106 //e.stopPropagation();
18111 this.onTickableFooterButtonClick(e, false, false);
18119 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18121 expand : function(){
18123 if(this.isExpanded() || !this.hasFocus){
18127 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18128 this.list.setWidth(lw);
18134 this.restrictHeight();
18138 this.tickItems = Roo.apply([], this.item);
18141 this.cancelBtn.show();
18142 this.trigger.hide();
18145 this.tickableInputEl().focus();
18150 Roo.get(document).on('mousedown', this.collapseIf, this);
18151 Roo.get(document).on('mousewheel', this.collapseIf, this);
18152 if (!this.editable) {
18153 Roo.get(document).on('keydown', this.listKeyPress, this);
18156 this.fireEvent('expand', this);
18160 // Implements the default empty TriggerField.onTriggerClick function
18161 onTriggerClick : function(e)
18163 Roo.log('trigger click');
18165 if(this.disabled || !this.triggerList){
18170 this.loadNext = false;
18172 if(this.isExpanded()){
18174 if (!this.blockFocus) {
18175 this.inputEl().focus();
18179 this.hasFocus = true;
18180 if(this.triggerAction == 'all') {
18181 this.doQuery(this.allQuery, true);
18183 this.doQuery(this.getRawValue());
18185 if (!this.blockFocus) {
18186 this.inputEl().focus();
18191 onTickableTriggerClick : function(e)
18198 this.loadNext = false;
18199 this.hasFocus = true;
18201 if(this.triggerAction == 'all') {
18202 this.doQuery(this.allQuery, true);
18204 this.doQuery(this.getRawValue());
18208 onSearchFieldClick : function(e)
18210 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18211 this.onTickableFooterButtonClick(e, false, false);
18215 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18220 this.loadNext = false;
18221 this.hasFocus = true;
18223 if(this.triggerAction == 'all') {
18224 this.doQuery(this.allQuery, true);
18226 this.doQuery(this.getRawValue());
18230 listKeyPress : function(e)
18232 //Roo.log('listkeypress');
18233 // scroll to first matching element based on key pres..
18234 if (e.isSpecialKey()) {
18237 var k = String.fromCharCode(e.getKey()).toUpperCase();
18240 var csel = this.view.getSelectedNodes();
18241 var cselitem = false;
18243 var ix = this.view.indexOf(csel[0]);
18244 cselitem = this.store.getAt(ix);
18245 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18251 this.store.each(function(v) {
18253 // start at existing selection.
18254 if (cselitem.id == v.id) {
18260 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18261 match = this.store.indexOf(v);
18267 if (match === false) {
18268 return true; // no more action?
18271 this.view.select(match);
18272 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18273 sn.scrollIntoView(sn.dom.parentNode, false);
18276 onViewScroll : function(e, t){
18278 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){
18282 this.hasQuery = true;
18284 this.loading = this.list.select('.loading', true).first();
18286 if(this.loading === null){
18287 this.list.createChild({
18289 cls: 'loading roo-select2-more-results roo-select2-active',
18290 html: 'Loading more results...'
18293 this.loading = this.list.select('.loading', true).first();
18295 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18297 this.loading.hide();
18300 this.loading.show();
18305 this.loadNext = true;
18307 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18312 addItem : function(o)
18314 var dv = ''; // display value
18316 if (this.displayField) {
18317 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18319 // this is an error condition!!!
18320 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18327 var choice = this.choices.createChild({
18329 cls: 'roo-select2-search-choice',
18338 cls: 'roo-select2-search-choice-close fa fa-times',
18343 }, this.searchField);
18345 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18347 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18355 this.inputEl().dom.value = '';
18360 onRemoveItem : function(e, _self, o)
18362 e.preventDefault();
18364 this.lastItem = Roo.apply([], this.item);
18366 var index = this.item.indexOf(o.data) * 1;
18369 Roo.log('not this item?!');
18373 this.item.splice(index, 1);
18378 this.fireEvent('remove', this, e);
18384 syncValue : function()
18386 if(!this.item.length){
18393 Roo.each(this.item, function(i){
18394 if(_this.valueField){
18395 value.push(i[_this.valueField]);
18402 this.value = value.join(',');
18404 if(this.hiddenField){
18405 this.hiddenField.dom.value = this.value;
18408 this.store.fireEvent("datachanged", this.store);
18413 clearItem : function()
18415 if(!this.multiple){
18421 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18429 if(this.tickable && !Roo.isTouch){
18430 this.view.refresh();
18434 inputEl: function ()
18436 if(Roo.isIOS && this.useNativeIOS){
18437 return this.el.select('select.roo-ios-select', true).first();
18440 if(Roo.isTouch && this.mobileTouchView){
18441 return this.el.select('input.form-control',true).first();
18445 return this.searchField;
18448 return this.el.select('input.form-control',true).first();
18451 onTickableFooterButtonClick : function(e, btn, el)
18453 e.preventDefault();
18455 this.lastItem = Roo.apply([], this.item);
18457 if(btn && btn.name == 'cancel'){
18458 this.tickItems = Roo.apply([], this.item);
18467 Roo.each(this.tickItems, function(o){
18475 validate : function()
18477 if(this.getVisibilityEl().hasClass('hidden')){
18481 var v = this.getRawValue();
18484 v = this.getValue();
18487 if(this.disabled || this.allowBlank || v.length){
18492 this.markInvalid();
18496 tickableInputEl : function()
18498 if(!this.tickable || !this.editable){
18499 return this.inputEl();
18502 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18506 getAutoCreateTouchView : function()
18511 cls: 'form-group' //input-group
18517 type : this.inputType,
18518 cls : 'form-control x-combo-noedit',
18519 autocomplete: 'new-password',
18520 placeholder : this.placeholder || '',
18525 input.name = this.name;
18529 input.cls += ' input-' + this.size;
18532 if (this.disabled) {
18533 input.disabled = true;
18537 cls : 'roo-combobox-wrap',
18544 inputblock.cls += ' input-group';
18546 inputblock.cn.unshift({
18548 cls : 'input-group-addon input-group-prepend input-group-text',
18553 if(this.removable && !this.multiple){
18554 inputblock.cls += ' roo-removable';
18556 inputblock.cn.push({
18559 cls : 'roo-combo-removable-btn close'
18563 if(this.hasFeedback && !this.allowBlank){
18565 inputblock.cls += ' has-feedback';
18567 inputblock.cn.push({
18569 cls: 'glyphicon form-control-feedback'
18576 inputblock.cls += (this.before) ? '' : ' input-group';
18578 inputblock.cn.push({
18580 cls : 'input-group-addon input-group-append input-group-text',
18586 var ibwrap = inputblock;
18591 cls: 'roo-select2-choices',
18595 cls: 'roo-select2-search-field',
18608 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18613 cls: 'form-hidden-field'
18619 if(!this.multiple && this.showToggleBtn){
18625 if (this.caret != false) {
18628 cls: 'fa fa-' + this.caret
18635 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18637 Roo.bootstrap.version == 3 ? caret : '',
18640 cls: 'combobox-clear',
18654 combobox.cls += ' roo-select2-container-multi';
18657 var required = this.allowBlank ? {
18659 style: 'display: none'
18662 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18663 tooltip : 'This field is required'
18666 var align = this.labelAlign || this.parentLabelAlign();
18668 if (align ==='left' && this.fieldLabel.length) {
18674 cls : 'control-label col-form-label',
18675 html : this.fieldLabel
18679 cls : 'roo-combobox-wrap ',
18686 var labelCfg = cfg.cn[1];
18687 var contentCfg = cfg.cn[2];
18690 if(this.indicatorpos == 'right'){
18695 cls : 'control-label col-form-label',
18699 html : this.fieldLabel
18705 cls : "roo-combobox-wrap ",
18713 labelCfg = cfg.cn[0];
18714 contentCfg = cfg.cn[1];
18719 if(this.labelWidth > 12){
18720 labelCfg.style = "width: " + this.labelWidth + 'px';
18723 if(this.labelWidth < 13 && this.labelmd == 0){
18724 this.labelmd = this.labelWidth;
18727 if(this.labellg > 0){
18728 labelCfg.cls += ' col-lg-' + this.labellg;
18729 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18732 if(this.labelmd > 0){
18733 labelCfg.cls += ' col-md-' + this.labelmd;
18734 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18737 if(this.labelsm > 0){
18738 labelCfg.cls += ' col-sm-' + this.labelsm;
18739 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18742 if(this.labelxs > 0){
18743 labelCfg.cls += ' col-xs-' + this.labelxs;
18744 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18748 } else if ( this.fieldLabel.length) {
18753 cls : 'control-label',
18754 html : this.fieldLabel
18765 if(this.indicatorpos == 'right'){
18769 cls : 'control-label',
18770 html : this.fieldLabel,
18788 var settings = this;
18790 ['xs','sm','md','lg'].map(function(size){
18791 if (settings[size]) {
18792 cfg.cls += ' col-' + size + '-' + settings[size];
18799 initTouchView : function()
18801 this.renderTouchView();
18803 this.touchViewEl.on('scroll', function(){
18804 this.el.dom.scrollTop = 0;
18807 this.originalValue = this.getValue();
18809 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18811 this.inputEl().on("click", this.showTouchView, this);
18812 if (this.triggerEl) {
18813 this.triggerEl.on("click", this.showTouchView, this);
18817 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18818 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18820 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18822 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18823 this.store.on('load', this.onTouchViewLoad, this);
18824 this.store.on('loadexception', this.onTouchViewLoadException, this);
18826 if(this.hiddenName){
18828 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18830 this.hiddenField.dom.value =
18831 this.hiddenValue !== undefined ? this.hiddenValue :
18832 this.value !== undefined ? this.value : '';
18834 this.el.dom.removeAttribute('name');
18835 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18839 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18840 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18843 if(this.removable && !this.multiple){
18844 var close = this.closeTriggerEl();
18846 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18847 close.on('click', this.removeBtnClick, this, close);
18851 * fix the bug in Safari iOS8
18853 this.inputEl().on("focus", function(e){
18854 document.activeElement.blur();
18857 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18864 renderTouchView : function()
18866 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18867 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18869 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18870 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18872 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18873 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18874 this.touchViewBodyEl.setStyle('overflow', 'auto');
18876 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18877 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18879 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18880 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18884 showTouchView : function()
18890 this.touchViewHeaderEl.hide();
18892 if(this.modalTitle.length){
18893 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18894 this.touchViewHeaderEl.show();
18897 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18898 this.touchViewEl.show();
18900 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18902 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18903 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18905 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18907 if(this.modalTitle.length){
18908 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18911 this.touchViewBodyEl.setHeight(bodyHeight);
18915 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18917 this.touchViewEl.addClass(['in','show']);
18920 if(this._touchViewMask){
18921 Roo.get(document.body).addClass("x-body-masked");
18922 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18923 this._touchViewMask.setStyle('z-index', 10000);
18924 this._touchViewMask.addClass('show');
18927 this.doTouchViewQuery();
18931 hideTouchView : function()
18933 this.touchViewEl.removeClass(['in','show']);
18937 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18939 this.touchViewEl.setStyle('display', 'none');
18942 if(this._touchViewMask){
18943 this._touchViewMask.removeClass('show');
18944 Roo.get(document.body).removeClass("x-body-masked");
18948 setTouchViewValue : function()
18955 Roo.each(this.tickItems, function(o){
18960 this.hideTouchView();
18963 doTouchViewQuery : function()
18972 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18976 if(!this.alwaysQuery || this.mode == 'local'){
18977 this.onTouchViewLoad();
18984 onTouchViewBeforeLoad : function(combo,opts)
18990 onTouchViewLoad : function()
18992 if(this.store.getCount() < 1){
18993 this.onTouchViewEmptyResults();
18997 this.clearTouchView();
18999 var rawValue = this.getRawValue();
19001 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19003 this.tickItems = [];
19005 this.store.data.each(function(d, rowIndex){
19006 var row = this.touchViewListGroup.createChild(template);
19008 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19009 row.addClass(d.data.cls);
19012 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19015 html : d.data[this.displayField]
19018 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19019 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19022 row.removeClass('selected');
19023 if(!this.multiple && this.valueField &&
19024 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19027 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19028 row.addClass('selected');
19031 if(this.multiple && this.valueField &&
19032 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19036 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19037 this.tickItems.push(d.data);
19040 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19044 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19046 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19048 if(this.modalTitle.length){
19049 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19052 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19054 if(this.mobile_restrict_height && listHeight < bodyHeight){
19055 this.touchViewBodyEl.setHeight(listHeight);
19060 if(firstChecked && listHeight > bodyHeight){
19061 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19066 onTouchViewLoadException : function()
19068 this.hideTouchView();
19071 onTouchViewEmptyResults : function()
19073 this.clearTouchView();
19075 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19077 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19081 clearTouchView : function()
19083 this.touchViewListGroup.dom.innerHTML = '';
19086 onTouchViewClick : function(e, el, o)
19088 e.preventDefault();
19091 var rowIndex = o.rowIndex;
19093 var r = this.store.getAt(rowIndex);
19095 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19097 if(!this.multiple){
19098 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19099 c.dom.removeAttribute('checked');
19102 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19104 this.setFromData(r.data);
19106 var close = this.closeTriggerEl();
19112 this.hideTouchView();
19114 this.fireEvent('select', this, r, rowIndex);
19119 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19120 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19121 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19125 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19126 this.addItem(r.data);
19127 this.tickItems.push(r.data);
19131 getAutoCreateNativeIOS : function()
19134 cls: 'form-group' //input-group,
19139 cls : 'roo-ios-select'
19143 combobox.name = this.name;
19146 if (this.disabled) {
19147 combobox.disabled = true;
19150 var settings = this;
19152 ['xs','sm','md','lg'].map(function(size){
19153 if (settings[size]) {
19154 cfg.cls += ' col-' + size + '-' + settings[size];
19164 initIOSView : function()
19166 this.store.on('load', this.onIOSViewLoad, this);
19171 onIOSViewLoad : function()
19173 if(this.store.getCount() < 1){
19177 this.clearIOSView();
19179 if(this.allowBlank) {
19181 var default_text = '-- SELECT --';
19183 if(this.placeholder.length){
19184 default_text = this.placeholder;
19187 if(this.emptyTitle.length){
19188 default_text += ' - ' + this.emptyTitle + ' -';
19191 var opt = this.inputEl().createChild({
19194 html : default_text
19198 o[this.valueField] = 0;
19199 o[this.displayField] = default_text;
19201 this.ios_options.push({
19208 this.store.data.each(function(d, rowIndex){
19212 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19213 html = d.data[this.displayField];
19218 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19219 value = d.data[this.valueField];
19228 if(this.value == d.data[this.valueField]){
19229 option['selected'] = true;
19232 var opt = this.inputEl().createChild(option);
19234 this.ios_options.push({
19241 this.inputEl().on('change', function(){
19242 this.fireEvent('select', this);
19247 clearIOSView: function()
19249 this.inputEl().dom.innerHTML = '';
19251 this.ios_options = [];
19254 setIOSValue: function(v)
19258 if(!this.ios_options){
19262 Roo.each(this.ios_options, function(opts){
19264 opts.el.dom.removeAttribute('selected');
19266 if(opts.data[this.valueField] != v){
19270 opts.el.dom.setAttribute('selected', true);
19276 * @cfg {Boolean} grow
19280 * @cfg {Number} growMin
19284 * @cfg {Number} growMax
19293 Roo.apply(Roo.bootstrap.ComboBox, {
19297 cls: 'modal-header',
19319 cls: 'list-group-item',
19323 cls: 'roo-combobox-list-group-item-value'
19327 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19341 listItemCheckbox : {
19343 cls: 'list-group-item',
19347 cls: 'roo-combobox-list-group-item-value'
19351 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19367 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19372 cls: 'modal-footer',
19380 cls: 'col-xs-6 text-left',
19383 cls: 'btn btn-danger roo-touch-view-cancel',
19389 cls: 'col-xs-6 text-right',
19392 cls: 'btn btn-success roo-touch-view-ok',
19403 Roo.apply(Roo.bootstrap.ComboBox, {
19405 touchViewTemplate : {
19407 cls: 'modal fade roo-combobox-touch-view',
19411 cls: 'modal-dialog',
19412 style : 'position:fixed', // we have to fix position....
19416 cls: 'modal-content',
19418 Roo.bootstrap.ComboBox.header,
19419 Roo.bootstrap.ComboBox.body,
19420 Roo.bootstrap.ComboBox.footer
19429 * Ext JS Library 1.1.1
19430 * Copyright(c) 2006-2007, Ext JS, LLC.
19432 * Originally Released Under LGPL - original licence link has changed is not relivant.
19435 * <script type="text/javascript">
19440 * @extends Roo.util.Observable
19441 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19442 * This class also supports single and multi selection modes. <br>
19443 * Create a data model bound view:
19445 var store = new Roo.data.Store(...);
19447 var view = new Roo.View({
19449 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19451 singleSelect: true,
19452 selectedClass: "ydataview-selected",
19456 // listen for node click?
19457 view.on("click", function(vw, index, node, e){
19458 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19462 dataModel.load("foobar.xml");
19464 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19466 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19467 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19469 * Note: old style constructor is still suported (container, template, config)
19472 * Create a new View
19473 * @param {Object} config The config object
19476 Roo.View = function(config, depreciated_tpl, depreciated_config){
19478 this.parent = false;
19480 if (typeof(depreciated_tpl) == 'undefined') {
19481 // new way.. - universal constructor.
19482 Roo.apply(this, config);
19483 this.el = Roo.get(this.el);
19486 this.el = Roo.get(config);
19487 this.tpl = depreciated_tpl;
19488 Roo.apply(this, depreciated_config);
19490 this.wrapEl = this.el.wrap().wrap();
19491 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19494 if(typeof(this.tpl) == "string"){
19495 this.tpl = new Roo.Template(this.tpl);
19497 // support xtype ctors..
19498 this.tpl = new Roo.factory(this.tpl, Roo);
19502 this.tpl.compile();
19507 * @event beforeclick
19508 * Fires before a click is processed. Returns false to cancel the default action.
19509 * @param {Roo.View} this
19510 * @param {Number} index The index of the target node
19511 * @param {HTMLElement} node The target node
19512 * @param {Roo.EventObject} e The raw event object
19514 "beforeclick" : true,
19517 * Fires when a template node is clicked.
19518 * @param {Roo.View} this
19519 * @param {Number} index The index of the target node
19520 * @param {HTMLElement} node The target node
19521 * @param {Roo.EventObject} e The raw event object
19526 * Fires when a template node is double clicked.
19527 * @param {Roo.View} this
19528 * @param {Number} index The index of the target node
19529 * @param {HTMLElement} node The target node
19530 * @param {Roo.EventObject} e The raw event object
19534 * @event contextmenu
19535 * Fires when a template node is right clicked.
19536 * @param {Roo.View} this
19537 * @param {Number} index The index of the target node
19538 * @param {HTMLElement} node The target node
19539 * @param {Roo.EventObject} e The raw event object
19541 "contextmenu" : true,
19543 * @event selectionchange
19544 * Fires when the selected nodes change.
19545 * @param {Roo.View} this
19546 * @param {Array} selections Array of the selected nodes
19548 "selectionchange" : true,
19551 * @event beforeselect
19552 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19553 * @param {Roo.View} this
19554 * @param {HTMLElement} node The node to be selected
19555 * @param {Array} selections Array of currently selected nodes
19557 "beforeselect" : true,
19559 * @event preparedata
19560 * Fires on every row to render, to allow you to change the data.
19561 * @param {Roo.View} this
19562 * @param {Object} data to be rendered (change this)
19564 "preparedata" : true
19572 "click": this.onClick,
19573 "dblclick": this.onDblClick,
19574 "contextmenu": this.onContextMenu,
19578 this.selections = [];
19580 this.cmp = new Roo.CompositeElementLite([]);
19582 this.store = Roo.factory(this.store, Roo.data);
19583 this.setStore(this.store, true);
19586 if ( this.footer && this.footer.xtype) {
19588 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19590 this.footer.dataSource = this.store;
19591 this.footer.container = fctr;
19592 this.footer = Roo.factory(this.footer, Roo);
19593 fctr.insertFirst(this.el);
19595 // this is a bit insane - as the paging toolbar seems to detach the el..
19596 // dom.parentNode.parentNode.parentNode
19597 // they get detached?
19601 Roo.View.superclass.constructor.call(this);
19606 Roo.extend(Roo.View, Roo.util.Observable, {
19609 * @cfg {Roo.data.Store} store Data store to load data from.
19614 * @cfg {String|Roo.Element} el The container element.
19619 * @cfg {String|Roo.Template} tpl The template used by this View
19623 * @cfg {String} dataName the named area of the template to use as the data area
19624 * Works with domtemplates roo-name="name"
19628 * @cfg {String} selectedClass The css class to add to selected nodes
19630 selectedClass : "x-view-selected",
19632 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19637 * @cfg {String} text to display on mask (default Loading)
19641 * @cfg {Boolean} multiSelect Allow multiple selection
19643 multiSelect : false,
19645 * @cfg {Boolean} singleSelect Allow single selection
19647 singleSelect: false,
19650 * @cfg {Boolean} toggleSelect - selecting
19652 toggleSelect : false,
19655 * @cfg {Boolean} tickable - selecting
19660 * Returns the element this view is bound to.
19661 * @return {Roo.Element}
19663 getEl : function(){
19664 return this.wrapEl;
19670 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19672 refresh : function(){
19673 //Roo.log('refresh');
19676 // if we are using something like 'domtemplate', then
19677 // the what gets used is:
19678 // t.applySubtemplate(NAME, data, wrapping data..)
19679 // the outer template then get' applied with
19680 // the store 'extra data'
19681 // and the body get's added to the
19682 // roo-name="data" node?
19683 // <span class='roo-tpl-{name}'></span> ?????
19687 this.clearSelections();
19688 this.el.update("");
19690 var records = this.store.getRange();
19691 if(records.length < 1) {
19693 // is this valid?? = should it render a template??
19695 this.el.update(this.emptyText);
19699 if (this.dataName) {
19700 this.el.update(t.apply(this.store.meta)); //????
19701 el = this.el.child('.roo-tpl-' + this.dataName);
19704 for(var i = 0, len = records.length; i < len; i++){
19705 var data = this.prepareData(records[i].data, i, records[i]);
19706 this.fireEvent("preparedata", this, data, i, records[i]);
19708 var d = Roo.apply({}, data);
19711 Roo.apply(d, {'roo-id' : Roo.id()});
19715 Roo.each(this.parent.item, function(item){
19716 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19719 Roo.apply(d, {'roo-data-checked' : 'checked'});
19723 html[html.length] = Roo.util.Format.trim(
19725 t.applySubtemplate(this.dataName, d, this.store.meta) :
19732 el.update(html.join(""));
19733 this.nodes = el.dom.childNodes;
19734 this.updateIndexes(0);
19739 * Function to override to reformat the data that is sent to
19740 * the template for each node.
19741 * DEPRICATED - use the preparedata event handler.
19742 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19743 * a JSON object for an UpdateManager bound view).
19745 prepareData : function(data, index, record)
19747 this.fireEvent("preparedata", this, data, index, record);
19751 onUpdate : function(ds, record){
19752 // Roo.log('on update');
19753 this.clearSelections();
19754 var index = this.store.indexOf(record);
19755 var n = this.nodes[index];
19756 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19757 n.parentNode.removeChild(n);
19758 this.updateIndexes(index, index);
19764 onAdd : function(ds, records, index)
19766 //Roo.log(['on Add', ds, records, index] );
19767 this.clearSelections();
19768 if(this.nodes.length == 0){
19772 var n = this.nodes[index];
19773 for(var i = 0, len = records.length; i < len; i++){
19774 var d = this.prepareData(records[i].data, i, records[i]);
19776 this.tpl.insertBefore(n, d);
19779 this.tpl.append(this.el, d);
19782 this.updateIndexes(index);
19785 onRemove : function(ds, record, index){
19786 // Roo.log('onRemove');
19787 this.clearSelections();
19788 var el = this.dataName ?
19789 this.el.child('.roo-tpl-' + this.dataName) :
19792 el.dom.removeChild(this.nodes[index]);
19793 this.updateIndexes(index);
19797 * Refresh an individual node.
19798 * @param {Number} index
19800 refreshNode : function(index){
19801 this.onUpdate(this.store, this.store.getAt(index));
19804 updateIndexes : function(startIndex, endIndex){
19805 var ns = this.nodes;
19806 startIndex = startIndex || 0;
19807 endIndex = endIndex || ns.length - 1;
19808 for(var i = startIndex; i <= endIndex; i++){
19809 ns[i].nodeIndex = i;
19814 * Changes the data store this view uses and refresh the view.
19815 * @param {Store} store
19817 setStore : function(store, initial){
19818 if(!initial && this.store){
19819 this.store.un("datachanged", this.refresh);
19820 this.store.un("add", this.onAdd);
19821 this.store.un("remove", this.onRemove);
19822 this.store.un("update", this.onUpdate);
19823 this.store.un("clear", this.refresh);
19824 this.store.un("beforeload", this.onBeforeLoad);
19825 this.store.un("load", this.onLoad);
19826 this.store.un("loadexception", this.onLoad);
19830 store.on("datachanged", this.refresh, this);
19831 store.on("add", this.onAdd, this);
19832 store.on("remove", this.onRemove, this);
19833 store.on("update", this.onUpdate, this);
19834 store.on("clear", this.refresh, this);
19835 store.on("beforeload", this.onBeforeLoad, this);
19836 store.on("load", this.onLoad, this);
19837 store.on("loadexception", this.onLoad, this);
19845 * onbeforeLoad - masks the loading area.
19848 onBeforeLoad : function(store,opts)
19850 //Roo.log('onBeforeLoad');
19852 this.el.update("");
19854 this.el.mask(this.mask ? this.mask : "Loading" );
19856 onLoad : function ()
19863 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19864 * @param {HTMLElement} node
19865 * @return {HTMLElement} The template node
19867 findItemFromChild : function(node){
19868 var el = this.dataName ?
19869 this.el.child('.roo-tpl-' + this.dataName,true) :
19872 if(!node || node.parentNode == el){
19875 var p = node.parentNode;
19876 while(p && p != el){
19877 if(p.parentNode == el){
19886 onClick : function(e){
19887 var item = this.findItemFromChild(e.getTarget());
19889 var index = this.indexOf(item);
19890 if(this.onItemClick(item, index, e) !== false){
19891 this.fireEvent("click", this, index, item, e);
19894 this.clearSelections();
19899 onContextMenu : function(e){
19900 var item = this.findItemFromChild(e.getTarget());
19902 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19907 onDblClick : function(e){
19908 var item = this.findItemFromChild(e.getTarget());
19910 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19914 onItemClick : function(item, index, e)
19916 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19919 if (this.toggleSelect) {
19920 var m = this.isSelected(item) ? 'unselect' : 'select';
19923 _t[m](item, true, false);
19926 if(this.multiSelect || this.singleSelect){
19927 if(this.multiSelect && e.shiftKey && this.lastSelection){
19928 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19930 this.select(item, this.multiSelect && e.ctrlKey);
19931 this.lastSelection = item;
19934 if(!this.tickable){
19935 e.preventDefault();
19943 * Get the number of selected nodes.
19946 getSelectionCount : function(){
19947 return this.selections.length;
19951 * Get the currently selected nodes.
19952 * @return {Array} An array of HTMLElements
19954 getSelectedNodes : function(){
19955 return this.selections;
19959 * Get the indexes of the selected nodes.
19962 getSelectedIndexes : function(){
19963 var indexes = [], s = this.selections;
19964 for(var i = 0, len = s.length; i < len; i++){
19965 indexes.push(s[i].nodeIndex);
19971 * Clear all selections
19972 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19974 clearSelections : function(suppressEvent){
19975 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19976 this.cmp.elements = this.selections;
19977 this.cmp.removeClass(this.selectedClass);
19978 this.selections = [];
19979 if(!suppressEvent){
19980 this.fireEvent("selectionchange", this, this.selections);
19986 * Returns true if the passed node is selected
19987 * @param {HTMLElement/Number} node The node or node index
19988 * @return {Boolean}
19990 isSelected : function(node){
19991 var s = this.selections;
19995 node = this.getNode(node);
19996 return s.indexOf(node) !== -1;
20001 * @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
20002 * @param {Boolean} keepExisting (optional) true to keep existing selections
20003 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20005 select : function(nodeInfo, keepExisting, suppressEvent){
20006 if(nodeInfo instanceof Array){
20008 this.clearSelections(true);
20010 for(var i = 0, len = nodeInfo.length; i < len; i++){
20011 this.select(nodeInfo[i], true, true);
20015 var node = this.getNode(nodeInfo);
20016 if(!node || this.isSelected(node)){
20017 return; // already selected.
20020 this.clearSelections(true);
20023 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20024 Roo.fly(node).addClass(this.selectedClass);
20025 this.selections.push(node);
20026 if(!suppressEvent){
20027 this.fireEvent("selectionchange", this, this.selections);
20035 * @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
20036 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20037 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20039 unselect : function(nodeInfo, keepExisting, suppressEvent)
20041 if(nodeInfo instanceof Array){
20042 Roo.each(this.selections, function(s) {
20043 this.unselect(s, nodeInfo);
20047 var node = this.getNode(nodeInfo);
20048 if(!node || !this.isSelected(node)){
20049 //Roo.log("not selected");
20050 return; // not selected.
20054 Roo.each(this.selections, function(s) {
20056 Roo.fly(node).removeClass(this.selectedClass);
20063 this.selections= ns;
20064 this.fireEvent("selectionchange", this, this.selections);
20068 * Gets a template node.
20069 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20070 * @return {HTMLElement} The node or null if it wasn't found
20072 getNode : function(nodeInfo){
20073 if(typeof nodeInfo == "string"){
20074 return document.getElementById(nodeInfo);
20075 }else if(typeof nodeInfo == "number"){
20076 return this.nodes[nodeInfo];
20082 * Gets a range template nodes.
20083 * @param {Number} startIndex
20084 * @param {Number} endIndex
20085 * @return {Array} An array of nodes
20087 getNodes : function(start, end){
20088 var ns = this.nodes;
20089 start = start || 0;
20090 end = typeof end == "undefined" ? ns.length - 1 : end;
20093 for(var i = start; i <= end; i++){
20097 for(var i = start; i >= end; i--){
20105 * Finds the index of the passed node
20106 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20107 * @return {Number} The index of the node or -1
20109 indexOf : function(node){
20110 node = this.getNode(node);
20111 if(typeof node.nodeIndex == "number"){
20112 return node.nodeIndex;
20114 var ns = this.nodes;
20115 for(var i = 0, len = ns.length; i < len; i++){
20126 * based on jquery fullcalendar
20130 Roo.bootstrap = Roo.bootstrap || {};
20132 * @class Roo.bootstrap.Calendar
20133 * @extends Roo.bootstrap.Component
20134 * Bootstrap Calendar class
20135 * @cfg {Boolean} loadMask (true|false) default false
20136 * @cfg {Object} header generate the user specific header of the calendar, default false
20139 * Create a new Container
20140 * @param {Object} config The config object
20145 Roo.bootstrap.Calendar = function(config){
20146 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20150 * Fires when a date is selected
20151 * @param {DatePicker} this
20152 * @param {Date} date The selected date
20156 * @event monthchange
20157 * Fires when the displayed month changes
20158 * @param {DatePicker} this
20159 * @param {Date} date The selected month
20161 'monthchange': true,
20163 * @event evententer
20164 * Fires when mouse over an event
20165 * @param {Calendar} this
20166 * @param {event} Event
20168 'evententer': true,
20170 * @event eventleave
20171 * Fires when the mouse leaves an
20172 * @param {Calendar} this
20175 'eventleave': true,
20177 * @event eventclick
20178 * Fires when the mouse click an
20179 * @param {Calendar} this
20188 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20191 * @cfg {Number} startDay
20192 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20200 getAutoCreate : function(){
20203 var fc_button = function(name, corner, style, content ) {
20204 return Roo.apply({},{
20206 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20208 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20211 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20222 style : 'width:100%',
20229 cls : 'fc-header-left',
20231 fc_button('prev', 'left', 'arrow', '‹' ),
20232 fc_button('next', 'right', 'arrow', '›' ),
20233 { tag: 'span', cls: 'fc-header-space' },
20234 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20242 cls : 'fc-header-center',
20246 cls: 'fc-header-title',
20249 html : 'month / year'
20257 cls : 'fc-header-right',
20259 /* fc_button('month', 'left', '', 'month' ),
20260 fc_button('week', '', '', 'week' ),
20261 fc_button('day', 'right', '', 'day' )
20273 header = this.header;
20276 var cal_heads = function() {
20278 // fixme - handle this.
20280 for (var i =0; i < Date.dayNames.length; i++) {
20281 var d = Date.dayNames[i];
20284 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20285 html : d.substring(0,3)
20289 ret[0].cls += ' fc-first';
20290 ret[6].cls += ' fc-last';
20293 var cal_cell = function(n) {
20296 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20301 cls: 'fc-day-number',
20305 cls: 'fc-day-content',
20309 style: 'position: relative;' // height: 17px;
20321 var cal_rows = function() {
20324 for (var r = 0; r < 6; r++) {
20331 for (var i =0; i < Date.dayNames.length; i++) {
20332 var d = Date.dayNames[i];
20333 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20336 row.cn[0].cls+=' fc-first';
20337 row.cn[0].cn[0].style = 'min-height:90px';
20338 row.cn[6].cls+=' fc-last';
20342 ret[0].cls += ' fc-first';
20343 ret[4].cls += ' fc-prev-last';
20344 ret[5].cls += ' fc-last';
20351 cls: 'fc-border-separate',
20352 style : 'width:100%',
20360 cls : 'fc-first fc-last',
20378 cls : 'fc-content',
20379 style : "position: relative;",
20382 cls : 'fc-view fc-view-month fc-grid',
20383 style : 'position: relative',
20384 unselectable : 'on',
20387 cls : 'fc-event-container',
20388 style : 'position:absolute;z-index:8;top:0;left:0;'
20406 initEvents : function()
20409 throw "can not find store for calendar";
20415 style: "text-align:center",
20419 style: "background-color:white;width:50%;margin:250 auto",
20423 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20434 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20436 var size = this.el.select('.fc-content', true).first().getSize();
20437 this.maskEl.setSize(size.width, size.height);
20438 this.maskEl.enableDisplayMode("block");
20439 if(!this.loadMask){
20440 this.maskEl.hide();
20443 this.store = Roo.factory(this.store, Roo.data);
20444 this.store.on('load', this.onLoad, this);
20445 this.store.on('beforeload', this.onBeforeLoad, this);
20449 this.cells = this.el.select('.fc-day',true);
20450 //Roo.log(this.cells);
20451 this.textNodes = this.el.query('.fc-day-number');
20452 this.cells.addClassOnOver('fc-state-hover');
20454 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20455 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20456 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20457 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20459 this.on('monthchange', this.onMonthChange, this);
20461 this.update(new Date().clearTime());
20464 resize : function() {
20465 var sz = this.el.getSize();
20467 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20468 this.el.select('.fc-day-content div',true).setHeight(34);
20473 showPrevMonth : function(e){
20474 this.update(this.activeDate.add("mo", -1));
20476 showToday : function(e){
20477 this.update(new Date().clearTime());
20480 showNextMonth : function(e){
20481 this.update(this.activeDate.add("mo", 1));
20485 showPrevYear : function(){
20486 this.update(this.activeDate.add("y", -1));
20490 showNextYear : function(){
20491 this.update(this.activeDate.add("y", 1));
20496 update : function(date)
20498 var vd = this.activeDate;
20499 this.activeDate = date;
20500 // if(vd && this.el){
20501 // var t = date.getTime();
20502 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20503 // Roo.log('using add remove');
20505 // this.fireEvent('monthchange', this, date);
20507 // this.cells.removeClass("fc-state-highlight");
20508 // this.cells.each(function(c){
20509 // if(c.dateValue == t){
20510 // c.addClass("fc-state-highlight");
20511 // setTimeout(function(){
20512 // try{c.dom.firstChild.focus();}catch(e){}
20522 var days = date.getDaysInMonth();
20524 var firstOfMonth = date.getFirstDateOfMonth();
20525 var startingPos = firstOfMonth.getDay()-this.startDay;
20527 if(startingPos < this.startDay){
20531 var pm = date.add(Date.MONTH, -1);
20532 var prevStart = pm.getDaysInMonth()-startingPos;
20534 this.cells = this.el.select('.fc-day',true);
20535 this.textNodes = this.el.query('.fc-day-number');
20536 this.cells.addClassOnOver('fc-state-hover');
20538 var cells = this.cells.elements;
20539 var textEls = this.textNodes;
20541 Roo.each(cells, function(cell){
20542 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20545 days += startingPos;
20547 // convert everything to numbers so it's fast
20548 var day = 86400000;
20549 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20552 //Roo.log(prevStart);
20554 var today = new Date().clearTime().getTime();
20555 var sel = date.clearTime().getTime();
20556 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20557 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20558 var ddMatch = this.disabledDatesRE;
20559 var ddText = this.disabledDatesText;
20560 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20561 var ddaysText = this.disabledDaysText;
20562 var format = this.format;
20564 var setCellClass = function(cal, cell){
20568 //Roo.log('set Cell Class');
20570 var t = d.getTime();
20574 cell.dateValue = t;
20576 cell.className += " fc-today";
20577 cell.className += " fc-state-highlight";
20578 cell.title = cal.todayText;
20581 // disable highlight in other month..
20582 //cell.className += " fc-state-highlight";
20587 cell.className = " fc-state-disabled";
20588 cell.title = cal.minText;
20592 cell.className = " fc-state-disabled";
20593 cell.title = cal.maxText;
20597 if(ddays.indexOf(d.getDay()) != -1){
20598 cell.title = ddaysText;
20599 cell.className = " fc-state-disabled";
20602 if(ddMatch && format){
20603 var fvalue = d.dateFormat(format);
20604 if(ddMatch.test(fvalue)){
20605 cell.title = ddText.replace("%0", fvalue);
20606 cell.className = " fc-state-disabled";
20610 if (!cell.initialClassName) {
20611 cell.initialClassName = cell.dom.className;
20614 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20619 for(; i < startingPos; i++) {
20620 textEls[i].innerHTML = (++prevStart);
20621 d.setDate(d.getDate()+1);
20623 cells[i].className = "fc-past fc-other-month";
20624 setCellClass(this, cells[i]);
20629 for(; i < days; i++){
20630 intDay = i - startingPos + 1;
20631 textEls[i].innerHTML = (intDay);
20632 d.setDate(d.getDate()+1);
20634 cells[i].className = ''; // "x-date-active";
20635 setCellClass(this, cells[i]);
20639 for(; i < 42; i++) {
20640 textEls[i].innerHTML = (++extraDays);
20641 d.setDate(d.getDate()+1);
20643 cells[i].className = "fc-future fc-other-month";
20644 setCellClass(this, cells[i]);
20647 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20649 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20651 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20652 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20654 if(totalRows != 6){
20655 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20656 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20659 this.fireEvent('monthchange', this, date);
20663 if(!this.internalRender){
20664 var main = this.el.dom.firstChild;
20665 var w = main.offsetWidth;
20666 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20667 Roo.fly(main).setWidth(w);
20668 this.internalRender = true;
20669 // opera does not respect the auto grow header center column
20670 // then, after it gets a width opera refuses to recalculate
20671 // without a second pass
20672 if(Roo.isOpera && !this.secondPass){
20673 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20674 this.secondPass = true;
20675 this.update.defer(10, this, [date]);
20682 findCell : function(dt) {
20683 dt = dt.clearTime().getTime();
20685 this.cells.each(function(c){
20686 //Roo.log("check " +c.dateValue + '?=' + dt);
20687 if(c.dateValue == dt){
20697 findCells : function(ev) {
20698 var s = ev.start.clone().clearTime().getTime();
20700 var e= ev.end.clone().clearTime().getTime();
20703 this.cells.each(function(c){
20704 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20706 if(c.dateValue > e){
20709 if(c.dateValue < s){
20718 // findBestRow: function(cells)
20722 // for (var i =0 ; i < cells.length;i++) {
20723 // ret = Math.max(cells[i].rows || 0,ret);
20730 addItem : function(ev)
20732 // look for vertical location slot in
20733 var cells = this.findCells(ev);
20735 // ev.row = this.findBestRow(cells);
20737 // work out the location.
20741 for(var i =0; i < cells.length; i++) {
20743 cells[i].row = cells[0].row;
20746 cells[i].row = cells[i].row + 1;
20756 if (crow.start.getY() == cells[i].getY()) {
20758 crow.end = cells[i];
20775 cells[0].events.push(ev);
20777 this.calevents.push(ev);
20780 clearEvents: function() {
20782 if(!this.calevents){
20786 Roo.each(this.cells.elements, function(c){
20792 Roo.each(this.calevents, function(e) {
20793 Roo.each(e.els, function(el) {
20794 el.un('mouseenter' ,this.onEventEnter, this);
20795 el.un('mouseleave' ,this.onEventLeave, this);
20800 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20806 renderEvents: function()
20810 this.cells.each(function(c) {
20819 if(c.row != c.events.length){
20820 r = 4 - (4 - (c.row - c.events.length));
20823 c.events = ev.slice(0, r);
20824 c.more = ev.slice(r);
20826 if(c.more.length && c.more.length == 1){
20827 c.events.push(c.more.pop());
20830 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20834 this.cells.each(function(c) {
20836 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20839 for (var e = 0; e < c.events.length; e++){
20840 var ev = c.events[e];
20841 var rows = ev.rows;
20843 for(var i = 0; i < rows.length; i++) {
20845 // how many rows should it span..
20848 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20849 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20851 unselectable : "on",
20854 cls: 'fc-event-inner',
20858 // cls: 'fc-event-time',
20859 // html : cells.length > 1 ? '' : ev.time
20863 cls: 'fc-event-title',
20864 html : String.format('{0}', ev.title)
20871 cls: 'ui-resizable-handle ui-resizable-e',
20872 html : '  '
20879 cfg.cls += ' fc-event-start';
20881 if ((i+1) == rows.length) {
20882 cfg.cls += ' fc-event-end';
20885 var ctr = _this.el.select('.fc-event-container',true).first();
20886 var cg = ctr.createChild(cfg);
20888 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20889 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20891 var r = (c.more.length) ? 1 : 0;
20892 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20893 cg.setWidth(ebox.right - sbox.x -2);
20895 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20896 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20897 cg.on('click', _this.onEventClick, _this, ev);
20908 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20909 style : 'position: absolute',
20910 unselectable : "on",
20913 cls: 'fc-event-inner',
20917 cls: 'fc-event-title',
20925 cls: 'ui-resizable-handle ui-resizable-e',
20926 html : '  '
20932 var ctr = _this.el.select('.fc-event-container',true).first();
20933 var cg = ctr.createChild(cfg);
20935 var sbox = c.select('.fc-day-content',true).first().getBox();
20936 var ebox = c.select('.fc-day-content',true).first().getBox();
20938 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20939 cg.setWidth(ebox.right - sbox.x -2);
20941 cg.on('click', _this.onMoreEventClick, _this, c.more);
20951 onEventEnter: function (e, el,event,d) {
20952 this.fireEvent('evententer', this, el, event);
20955 onEventLeave: function (e, el,event,d) {
20956 this.fireEvent('eventleave', this, el, event);
20959 onEventClick: function (e, el,event,d) {
20960 this.fireEvent('eventclick', this, el, event);
20963 onMonthChange: function () {
20967 onMoreEventClick: function(e, el, more)
20971 this.calpopover.placement = 'right';
20972 this.calpopover.setTitle('More');
20974 this.calpopover.setContent('');
20976 var ctr = this.calpopover.el.select('.popover-content', true).first();
20978 Roo.each(more, function(m){
20980 cls : 'fc-event-hori fc-event-draggable',
20983 var cg = ctr.createChild(cfg);
20985 cg.on('click', _this.onEventClick, _this, m);
20988 this.calpopover.show(el);
20993 onLoad: function ()
20995 this.calevents = [];
20998 if(this.store.getCount() > 0){
20999 this.store.data.each(function(d){
21002 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21003 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21004 time : d.data.start_time,
21005 title : d.data.title,
21006 description : d.data.description,
21007 venue : d.data.venue
21012 this.renderEvents();
21014 if(this.calevents.length && this.loadMask){
21015 this.maskEl.hide();
21019 onBeforeLoad: function()
21021 this.clearEvents();
21023 this.maskEl.show();
21037 * @class Roo.bootstrap.Popover
21038 * @extends Roo.bootstrap.Component
21039 * Bootstrap Popover class
21040 * @cfg {String} html contents of the popover (or false to use children..)
21041 * @cfg {String} title of popover (or false to hide)
21042 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21043 * @cfg {String} trigger click || hover (or false to trigger manually)
21044 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21045 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21046 * - if false and it has a 'parent' then it will be automatically added to that element
21047 * - if string - Roo.get will be called
21048 * @cfg {Number} delay - delay before showing
21051 * Create a new Popover
21052 * @param {Object} config The config object
21055 Roo.bootstrap.Popover = function(config){
21056 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21062 * After the popover show
21064 * @param {Roo.bootstrap.Popover} this
21069 * After the popover hide
21071 * @param {Roo.bootstrap.Popover} this
21077 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21082 placement : 'right',
21083 trigger : 'hover', // hover
21089 can_build_overlaid : false,
21091 maskEl : false, // the mask element
21094 alignEl : false, // when show is called with an element - this get's stored.
21096 getChildContainer : function()
21098 return this.contentEl;
21101 getPopoverHeader : function()
21103 this.title = true; // flag not to hide it..
21104 this.headerEl.addClass('p-0');
21105 return this.headerEl
21109 getAutoCreate : function(){
21112 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21113 style: 'display:block',
21119 cls : 'popover-inner ',
21123 cls: 'popover-title popover-header',
21124 html : this.title === false ? '' : this.title
21127 cls : 'popover-content popover-body ' + (this.cls || ''),
21128 html : this.html || ''
21139 * @param {string} the title
21141 setTitle: function(str)
21145 this.headerEl.dom.innerHTML = str;
21150 * @param {string} the body content
21152 setContent: function(str)
21155 if (this.contentEl) {
21156 this.contentEl.dom.innerHTML = str;
21160 // as it get's added to the bottom of the page.
21161 onRender : function(ct, position)
21163 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21168 var cfg = Roo.apply({}, this.getAutoCreate());
21172 cfg.cls += ' ' + this.cls;
21175 cfg.style = this.style;
21177 //Roo.log("adding to ");
21178 this.el = Roo.get(document.body).createChild(cfg, position);
21179 // Roo.log(this.el);
21182 this.contentEl = this.el.select('.popover-content',true).first();
21183 this.headerEl = this.el.select('.popover-title',true).first();
21186 if(typeof(this.items) != 'undefined'){
21187 var items = this.items;
21190 for(var i =0;i < items.length;i++) {
21191 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21195 this.items = nitems;
21197 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21198 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21205 resizeMask : function()
21207 this.maskEl.setSize(
21208 Roo.lib.Dom.getViewWidth(true),
21209 Roo.lib.Dom.getViewHeight(true)
21213 initEvents : function()
21217 Roo.bootstrap.Popover.register(this);
21220 this.arrowEl = this.el.select('.arrow',true).first();
21221 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21222 this.el.enableDisplayMode('block');
21226 if (this.over === false && !this.parent()) {
21229 if (this.triggers === false) {
21234 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21235 var triggers = this.trigger ? this.trigger.split(' ') : [];
21236 Roo.each(triggers, function(trigger) {
21238 if (trigger == 'click') {
21239 on_el.on('click', this.toggle, this);
21240 } else if (trigger != 'manual') {
21241 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21242 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21244 on_el.on(eventIn ,this.enter, this);
21245 on_el.on(eventOut, this.leave, this);
21255 toggle : function () {
21256 this.hoverState == 'in' ? this.leave() : this.enter();
21259 enter : function () {
21261 clearTimeout(this.timeout);
21263 this.hoverState = 'in';
21265 if (!this.delay || !this.delay.show) {
21270 this.timeout = setTimeout(function () {
21271 if (_t.hoverState == 'in') {
21274 }, this.delay.show)
21277 leave : function() {
21278 clearTimeout(this.timeout);
21280 this.hoverState = 'out';
21282 if (!this.delay || !this.delay.hide) {
21287 this.timeout = setTimeout(function () {
21288 if (_t.hoverState == 'out') {
21291 }, this.delay.hide)
21295 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21296 * @param {string} (left|right|top|bottom) position
21298 show : function (on_el, placement)
21300 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21301 on_el = on_el || false; // default to false
21304 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21305 on_el = this.parent().el;
21306 } else if (this.over) {
21307 on_el = Roo.get(this.over);
21312 this.alignEl = Roo.get( on_el );
21315 this.render(document.body);
21321 if (this.title === false) {
21322 this.headerEl.hide();
21327 this.el.dom.style.display = 'block';
21330 if (this.alignEl) {
21331 this.updatePosition(this.placement, true);
21334 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21335 var es = this.el.getSize();
21336 var x = Roo.lib.Dom.getViewWidth()/2;
21337 var y = Roo.lib.Dom.getViewHeight()/2;
21338 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21343 //var arrow = this.el.select('.arrow',true).first();
21344 //arrow.set(align[2],
21346 this.el.addClass('in');
21350 this.hoverState = 'in';
21353 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21354 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21355 this.maskEl.dom.style.display = 'block';
21356 this.maskEl.addClass('show');
21358 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21360 this.fireEvent('show', this);
21364 * fire this manually after loading a grid in the table for example
21365 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21366 * @param {Boolean} try and move it if we cant get right position.
21368 updatePosition : function(placement, try_move)
21370 // allow for calling with no parameters
21371 placement = placement ? placement : this.placement;
21372 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21374 this.el.removeClass([
21375 'fade','top','bottom', 'left', 'right','in',
21376 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21378 this.el.addClass(placement + ' bs-popover-' + placement);
21380 if (!this.alignEl ) {
21384 switch (placement) {
21386 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21387 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21388 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21389 //normal display... or moved up/down.
21390 this.el.setXY(offset);
21391 var xy = this.alignEl.getAnchorXY('tr', false);
21393 this.arrowEl.setXY(xy);
21396 // continue through...
21397 return this.updatePosition('left', false);
21401 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21402 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21403 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21404 //normal display... or moved up/down.
21405 this.el.setXY(offset);
21406 var xy = this.alignEl.getAnchorXY('tl', false);
21407 xy[0]-=10;xy[1]+=5; // << fix me
21408 this.arrowEl.setXY(xy);
21412 return this.updatePosition('right', false);
21415 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21416 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21417 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21418 //normal display... or moved up/down.
21419 this.el.setXY(offset);
21420 var xy = this.alignEl.getAnchorXY('t', false);
21421 xy[1]-=10; // << fix me
21422 this.arrowEl.setXY(xy);
21426 return this.updatePosition('bottom', false);
21429 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21430 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21431 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21432 //normal display... or moved up/down.
21433 this.el.setXY(offset);
21434 var xy = this.alignEl.getAnchorXY('b', false);
21435 xy[1]+=2; // << fix me
21436 this.arrowEl.setXY(xy);
21440 return this.updatePosition('top', false);
21451 this.el.setXY([0,0]);
21452 this.el.removeClass('in');
21454 this.hoverState = null;
21455 this.maskEl.hide(); // always..
21456 this.fireEvent('hide', this);
21462 Roo.apply(Roo.bootstrap.Popover, {
21465 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21466 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21467 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21468 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21473 clickHander : false,
21477 onMouseDown : function(e)
21479 if (this.popups.length && !e.getTarget(".roo-popover")) {
21480 /// what is nothing is showing..
21489 register : function(popup)
21491 if (!Roo.bootstrap.Popover.clickHandler) {
21492 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21494 // hide other popups.
21495 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21496 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21497 this.hideAll(); //<< why?
21498 //this.popups.push(popup);
21500 hideAll : function()
21502 this.popups.forEach(function(p) {
21506 onShow : function() {
21507 Roo.bootstrap.Popover.popups.push(this);
21509 onHide : function() {
21510 Roo.bootstrap.Popover.popups.remove(this);
21516 * Card header - holder for the card header elements.
21521 * @class Roo.bootstrap.PopoverNav
21522 * @extends Roo.bootstrap.NavGroup
21523 * Bootstrap Popover header navigation class
21525 * Create a new Popover Header Navigation
21526 * @param {Object} config The config object
21529 Roo.bootstrap.PopoverNav = function(config){
21530 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21533 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21536 container_method : 'getPopoverHeader'
21554 * @class Roo.bootstrap.Progress
21555 * @extends Roo.bootstrap.Component
21556 * Bootstrap Progress class
21557 * @cfg {Boolean} striped striped of the progress bar
21558 * @cfg {Boolean} active animated of the progress bar
21562 * Create a new Progress
21563 * @param {Object} config The config object
21566 Roo.bootstrap.Progress = function(config){
21567 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21570 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21575 getAutoCreate : function(){
21583 cfg.cls += ' progress-striped';
21587 cfg.cls += ' active';
21606 * @class Roo.bootstrap.ProgressBar
21607 * @extends Roo.bootstrap.Component
21608 * Bootstrap ProgressBar class
21609 * @cfg {Number} aria_valuenow aria-value now
21610 * @cfg {Number} aria_valuemin aria-value min
21611 * @cfg {Number} aria_valuemax aria-value max
21612 * @cfg {String} label label for the progress bar
21613 * @cfg {String} panel (success | info | warning | danger )
21614 * @cfg {String} role role of the progress bar
21615 * @cfg {String} sr_only text
21619 * Create a new ProgressBar
21620 * @param {Object} config The config object
21623 Roo.bootstrap.ProgressBar = function(config){
21624 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21627 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21631 aria_valuemax : 100,
21637 getAutoCreate : function()
21642 cls: 'progress-bar',
21643 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21655 cfg.role = this.role;
21658 if(this.aria_valuenow){
21659 cfg['aria-valuenow'] = this.aria_valuenow;
21662 if(this.aria_valuemin){
21663 cfg['aria-valuemin'] = this.aria_valuemin;
21666 if(this.aria_valuemax){
21667 cfg['aria-valuemax'] = this.aria_valuemax;
21670 if(this.label && !this.sr_only){
21671 cfg.html = this.label;
21675 cfg.cls += ' progress-bar-' + this.panel;
21681 update : function(aria_valuenow)
21683 this.aria_valuenow = aria_valuenow;
21685 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21700 * @class Roo.bootstrap.TabGroup
21701 * @extends Roo.bootstrap.Column
21702 * Bootstrap Column class
21703 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21704 * @cfg {Boolean} carousel true to make the group behave like a carousel
21705 * @cfg {Boolean} bullets show bullets for the panels
21706 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21707 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21708 * @cfg {Boolean} showarrow (true|false) show arrow default true
21711 * Create a new TabGroup
21712 * @param {Object} config The config object
21715 Roo.bootstrap.TabGroup = function(config){
21716 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21718 this.navId = Roo.id();
21721 Roo.bootstrap.TabGroup.register(this);
21725 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21728 transition : false,
21733 slideOnTouch : false,
21736 getAutoCreate : function()
21738 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21740 cfg.cls += ' tab-content';
21742 if (this.carousel) {
21743 cfg.cls += ' carousel slide';
21746 cls : 'carousel-inner',
21750 if(this.bullets && !Roo.isTouch){
21753 cls : 'carousel-bullets',
21757 if(this.bullets_cls){
21758 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21765 cfg.cn[0].cn.push(bullets);
21768 if(this.showarrow){
21769 cfg.cn[0].cn.push({
21771 class : 'carousel-arrow',
21775 class : 'carousel-prev',
21779 class : 'fa fa-chevron-left'
21785 class : 'carousel-next',
21789 class : 'fa fa-chevron-right'
21802 initEvents: function()
21804 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21805 // this.el.on("touchstart", this.onTouchStart, this);
21808 if(this.autoslide){
21811 this.slideFn = window.setInterval(function() {
21812 _this.showPanelNext();
21816 if(this.showarrow){
21817 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21818 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21824 // onTouchStart : function(e, el, o)
21826 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21830 // this.showPanelNext();
21834 getChildContainer : function()
21836 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21840 * register a Navigation item
21841 * @param {Roo.bootstrap.NavItem} the navitem to add
21843 register : function(item)
21845 this.tabs.push( item);
21846 item.navId = this.navId; // not really needed..
21851 getActivePanel : function()
21854 Roo.each(this.tabs, function(t) {
21864 getPanelByName : function(n)
21867 Roo.each(this.tabs, function(t) {
21868 if (t.tabId == n) {
21876 indexOfPanel : function(p)
21879 Roo.each(this.tabs, function(t,i) {
21880 if (t.tabId == p.tabId) {
21889 * show a specific panel
21890 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21891 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21893 showPanel : function (pan)
21895 if(this.transition || typeof(pan) == 'undefined'){
21896 Roo.log("waiting for the transitionend");
21900 if (typeof(pan) == 'number') {
21901 pan = this.tabs[pan];
21904 if (typeof(pan) == 'string') {
21905 pan = this.getPanelByName(pan);
21908 var cur = this.getActivePanel();
21911 Roo.log('pan or acitve pan is undefined');
21915 if (pan.tabId == this.getActivePanel().tabId) {
21919 if (false === cur.fireEvent('beforedeactivate')) {
21923 if(this.bullets > 0 && !Roo.isTouch){
21924 this.setActiveBullet(this.indexOfPanel(pan));
21927 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21929 //class="carousel-item carousel-item-next carousel-item-left"
21931 this.transition = true;
21932 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21933 var lr = dir == 'next' ? 'left' : 'right';
21934 pan.el.addClass(dir); // or prev
21935 pan.el.addClass('carousel-item-' + dir); // or prev
21936 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21937 cur.el.addClass(lr); // or right
21938 pan.el.addClass(lr);
21939 cur.el.addClass('carousel-item-' +lr); // or right
21940 pan.el.addClass('carousel-item-' +lr);
21944 cur.el.on('transitionend', function() {
21945 Roo.log("trans end?");
21947 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21948 pan.setActive(true);
21950 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21951 cur.setActive(false);
21953 _this.transition = false;
21955 }, this, { single: true } );
21960 cur.setActive(false);
21961 pan.setActive(true);
21966 showPanelNext : function()
21968 var i = this.indexOfPanel(this.getActivePanel());
21970 if (i >= this.tabs.length - 1 && !this.autoslide) {
21974 if (i >= this.tabs.length - 1 && this.autoslide) {
21978 this.showPanel(this.tabs[i+1]);
21981 showPanelPrev : function()
21983 var i = this.indexOfPanel(this.getActivePanel());
21985 if (i < 1 && !this.autoslide) {
21989 if (i < 1 && this.autoslide) {
21990 i = this.tabs.length;
21993 this.showPanel(this.tabs[i-1]);
21997 addBullet: function()
21999 if(!this.bullets || Roo.isTouch){
22002 var ctr = this.el.select('.carousel-bullets',true).first();
22003 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22004 var bullet = ctr.createChild({
22005 cls : 'bullet bullet-' + i
22006 },ctr.dom.lastChild);
22011 bullet.on('click', (function(e, el, o, ii, t){
22013 e.preventDefault();
22015 this.showPanel(ii);
22017 if(this.autoslide && this.slideFn){
22018 clearInterval(this.slideFn);
22019 this.slideFn = window.setInterval(function() {
22020 _this.showPanelNext();
22024 }).createDelegate(this, [i, bullet], true));
22029 setActiveBullet : function(i)
22035 Roo.each(this.el.select('.bullet', true).elements, function(el){
22036 el.removeClass('selected');
22039 var bullet = this.el.select('.bullet-' + i, true).first();
22045 bullet.addClass('selected');
22056 Roo.apply(Roo.bootstrap.TabGroup, {
22060 * register a Navigation Group
22061 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22063 register : function(navgrp)
22065 this.groups[navgrp.navId] = navgrp;
22069 * fetch a Navigation Group based on the navigation ID
22070 * if one does not exist , it will get created.
22071 * @param {string} the navgroup to add
22072 * @returns {Roo.bootstrap.NavGroup} the navgroup
22074 get: function(navId) {
22075 if (typeof(this.groups[navId]) == 'undefined') {
22076 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22078 return this.groups[navId] ;
22093 * @class Roo.bootstrap.TabPanel
22094 * @extends Roo.bootstrap.Component
22095 * Bootstrap TabPanel class
22096 * @cfg {Boolean} active panel active
22097 * @cfg {String} html panel content
22098 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22099 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22100 * @cfg {String} href click to link..
22101 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22105 * Create a new TabPanel
22106 * @param {Object} config The config object
22109 Roo.bootstrap.TabPanel = function(config){
22110 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22114 * Fires when the active status changes
22115 * @param {Roo.bootstrap.TabPanel} this
22116 * @param {Boolean} state the new state
22121 * @event beforedeactivate
22122 * Fires before a tab is de-activated - can be used to do validation on a form.
22123 * @param {Roo.bootstrap.TabPanel} this
22124 * @return {Boolean} false if there is an error
22127 'beforedeactivate': true
22130 this.tabId = this.tabId || Roo.id();
22134 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22141 touchSlide : false,
22142 getAutoCreate : function(){
22147 // item is needed for carousel - not sure if it has any effect otherwise
22148 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22149 html: this.html || ''
22153 cfg.cls += ' active';
22157 cfg.tabId = this.tabId;
22165 initEvents: function()
22167 var p = this.parent();
22169 this.navId = this.navId || p.navId;
22171 if (typeof(this.navId) != 'undefined') {
22172 // not really needed.. but just in case.. parent should be a NavGroup.
22173 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22177 var i = tg.tabs.length - 1;
22179 if(this.active && tg.bullets > 0 && i < tg.bullets){
22180 tg.setActiveBullet(i);
22184 this.el.on('click', this.onClick, this);
22186 if(Roo.isTouch && this.touchSlide){
22187 this.el.on("touchstart", this.onTouchStart, this);
22188 this.el.on("touchmove", this.onTouchMove, this);
22189 this.el.on("touchend", this.onTouchEnd, this);
22194 onRender : function(ct, position)
22196 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22199 setActive : function(state)
22201 Roo.log("panel - set active " + this.tabId + "=" + state);
22203 this.active = state;
22205 this.el.removeClass('active');
22207 } else if (!this.el.hasClass('active')) {
22208 this.el.addClass('active');
22211 this.fireEvent('changed', this, state);
22214 onClick : function(e)
22216 e.preventDefault();
22218 if(!this.href.length){
22222 window.location.href = this.href;
22231 onTouchStart : function(e)
22233 this.swiping = false;
22235 this.startX = e.browserEvent.touches[0].clientX;
22236 this.startY = e.browserEvent.touches[0].clientY;
22239 onTouchMove : function(e)
22241 this.swiping = true;
22243 this.endX = e.browserEvent.touches[0].clientX;
22244 this.endY = e.browserEvent.touches[0].clientY;
22247 onTouchEnd : function(e)
22254 var tabGroup = this.parent();
22256 if(this.endX > this.startX){ // swiping right
22257 tabGroup.showPanelPrev();
22261 if(this.startX > this.endX){ // swiping left
22262 tabGroup.showPanelNext();
22281 * @class Roo.bootstrap.DateField
22282 * @extends Roo.bootstrap.Input
22283 * Bootstrap DateField class
22284 * @cfg {Number} weekStart default 0
22285 * @cfg {String} viewMode default empty, (months|years)
22286 * @cfg {String} minViewMode default empty, (months|years)
22287 * @cfg {Number} startDate default -Infinity
22288 * @cfg {Number} endDate default Infinity
22289 * @cfg {Boolean} todayHighlight default false
22290 * @cfg {Boolean} todayBtn default false
22291 * @cfg {Boolean} calendarWeeks default false
22292 * @cfg {Object} daysOfWeekDisabled default empty
22293 * @cfg {Boolean} singleMode default false (true | false)
22295 * @cfg {Boolean} keyboardNavigation default true
22296 * @cfg {String} language default en
22299 * Create a new DateField
22300 * @param {Object} config The config object
22303 Roo.bootstrap.DateField = function(config){
22304 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22308 * Fires when this field show.
22309 * @param {Roo.bootstrap.DateField} this
22310 * @param {Mixed} date The date value
22315 * Fires when this field hide.
22316 * @param {Roo.bootstrap.DateField} this
22317 * @param {Mixed} date The date value
22322 * Fires when select a date.
22323 * @param {Roo.bootstrap.DateField} this
22324 * @param {Mixed} date The date value
22328 * @event beforeselect
22329 * Fires when before select a date.
22330 * @param {Roo.bootstrap.DateField} this
22331 * @param {Mixed} date The date value
22333 beforeselect : true
22337 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22340 * @cfg {String} format
22341 * The default date format string which can be overriden for localization support. The format must be
22342 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22346 * @cfg {String} altFormats
22347 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22348 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22350 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22358 todayHighlight : false,
22364 keyboardNavigation: true,
22366 calendarWeeks: false,
22368 startDate: -Infinity,
22372 daysOfWeekDisabled: [],
22376 singleMode : false,
22378 UTCDate: function()
22380 return new Date(Date.UTC.apply(Date, arguments));
22383 UTCToday: function()
22385 var today = new Date();
22386 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22389 getDate: function() {
22390 var d = this.getUTCDate();
22391 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22394 getUTCDate: function() {
22398 setDate: function(d) {
22399 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22402 setUTCDate: function(d) {
22404 this.setValue(this.formatDate(this.date));
22407 onRender: function(ct, position)
22410 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22412 this.language = this.language || 'en';
22413 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22414 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22416 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22417 this.format = this.format || 'm/d/y';
22418 this.isInline = false;
22419 this.isInput = true;
22420 this.component = this.el.select('.add-on', true).first() || false;
22421 this.component = (this.component && this.component.length === 0) ? false : this.component;
22422 this.hasInput = this.component && this.inputEl().length;
22424 if (typeof(this.minViewMode === 'string')) {
22425 switch (this.minViewMode) {
22427 this.minViewMode = 1;
22430 this.minViewMode = 2;
22433 this.minViewMode = 0;
22438 if (typeof(this.viewMode === 'string')) {
22439 switch (this.viewMode) {
22452 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22454 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22456 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22458 this.picker().on('mousedown', this.onMousedown, this);
22459 this.picker().on('click', this.onClick, this);
22461 this.picker().addClass('datepicker-dropdown');
22463 this.startViewMode = this.viewMode;
22465 if(this.singleMode){
22466 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22467 v.setVisibilityMode(Roo.Element.DISPLAY);
22471 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22472 v.setStyle('width', '189px');
22476 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22477 if(!this.calendarWeeks){
22482 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22483 v.attr('colspan', function(i, val){
22484 return parseInt(val) + 1;
22489 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22491 this.setStartDate(this.startDate);
22492 this.setEndDate(this.endDate);
22494 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22501 if(this.isInline) {
22506 picker : function()
22508 return this.pickerEl;
22509 // return this.el.select('.datepicker', true).first();
22512 fillDow: function()
22514 var dowCnt = this.weekStart;
22523 if(this.calendarWeeks){
22531 while (dowCnt < this.weekStart + 7) {
22535 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22539 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22542 fillMonths: function()
22545 var months = this.picker().select('>.datepicker-months td', true).first();
22547 months.dom.innerHTML = '';
22553 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22556 months.createChild(month);
22563 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;
22565 if (this.date < this.startDate) {
22566 this.viewDate = new Date(this.startDate);
22567 } else if (this.date > this.endDate) {
22568 this.viewDate = new Date(this.endDate);
22570 this.viewDate = new Date(this.date);
22578 var d = new Date(this.viewDate),
22579 year = d.getUTCFullYear(),
22580 month = d.getUTCMonth(),
22581 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22582 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22583 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22584 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22585 currentDate = this.date && this.date.valueOf(),
22586 today = this.UTCToday();
22588 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22590 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22592 // this.picker.select('>tfoot th.today').
22593 // .text(dates[this.language].today)
22594 // .toggle(this.todayBtn !== false);
22596 this.updateNavArrows();
22599 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22601 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22603 prevMonth.setUTCDate(day);
22605 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22607 var nextMonth = new Date(prevMonth);
22609 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22611 nextMonth = nextMonth.valueOf();
22613 var fillMonths = false;
22615 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22617 while(prevMonth.valueOf() <= nextMonth) {
22620 if (prevMonth.getUTCDay() === this.weekStart) {
22622 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22630 if(this.calendarWeeks){
22631 // ISO 8601: First week contains first thursday.
22632 // ISO also states week starts on Monday, but we can be more abstract here.
22634 // Start of current week: based on weekstart/current date
22635 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22636 // Thursday of this week
22637 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22638 // First Thursday of year, year from thursday
22639 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22640 // Calendar week: ms between thursdays, div ms per day, div 7 days
22641 calWeek = (th - yth) / 864e5 / 7 + 1;
22643 fillMonths.cn.push({
22651 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22653 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22656 if (this.todayHighlight &&
22657 prevMonth.getUTCFullYear() == today.getFullYear() &&
22658 prevMonth.getUTCMonth() == today.getMonth() &&
22659 prevMonth.getUTCDate() == today.getDate()) {
22660 clsName += ' today';
22663 if (currentDate && prevMonth.valueOf() === currentDate) {
22664 clsName += ' active';
22667 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22668 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22669 clsName += ' disabled';
22672 fillMonths.cn.push({
22674 cls: 'day ' + clsName,
22675 html: prevMonth.getDate()
22678 prevMonth.setDate(prevMonth.getDate()+1);
22681 var currentYear = this.date && this.date.getUTCFullYear();
22682 var currentMonth = this.date && this.date.getUTCMonth();
22684 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22686 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22687 v.removeClass('active');
22689 if(currentYear === year && k === currentMonth){
22690 v.addClass('active');
22693 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22694 v.addClass('disabled');
22700 year = parseInt(year/10, 10) * 10;
22702 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22704 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22707 for (var i = -1; i < 11; i++) {
22708 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22710 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22718 showMode: function(dir)
22721 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22724 Roo.each(this.picker().select('>div',true).elements, function(v){
22725 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22728 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22733 if(this.isInline) {
22737 this.picker().removeClass(['bottom', 'top']);
22739 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22741 * place to the top of element!
22745 this.picker().addClass('top');
22746 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22751 this.picker().addClass('bottom');
22753 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22756 parseDate : function(value)
22758 if(!value || value instanceof Date){
22761 var v = Date.parseDate(value, this.format);
22762 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22763 v = Date.parseDate(value, 'Y-m-d');
22765 if(!v && this.altFormats){
22766 if(!this.altFormatsArray){
22767 this.altFormatsArray = this.altFormats.split("|");
22769 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22770 v = Date.parseDate(value, this.altFormatsArray[i]);
22776 formatDate : function(date, fmt)
22778 return (!date || !(date instanceof Date)) ?
22779 date : date.dateFormat(fmt || this.format);
22782 onFocus : function()
22784 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22788 onBlur : function()
22790 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22792 var d = this.inputEl().getValue();
22799 showPopup : function()
22801 this.picker().show();
22805 this.fireEvent('showpopup', this, this.date);
22808 hidePopup : function()
22810 if(this.isInline) {
22813 this.picker().hide();
22814 this.viewMode = this.startViewMode;
22817 this.fireEvent('hidepopup', this, this.date);
22821 onMousedown: function(e)
22823 e.stopPropagation();
22824 e.preventDefault();
22829 Roo.bootstrap.DateField.superclass.keyup.call(this);
22833 setValue: function(v)
22835 if(this.fireEvent('beforeselect', this, v) !== false){
22836 var d = new Date(this.parseDate(v) ).clearTime();
22838 if(isNaN(d.getTime())){
22839 this.date = this.viewDate = '';
22840 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22844 v = this.formatDate(d);
22846 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22848 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22852 this.fireEvent('select', this, this.date);
22856 getValue: function()
22858 return this.formatDate(this.date);
22861 fireKey: function(e)
22863 if (!this.picker().isVisible()){
22864 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22870 var dateChanged = false,
22872 newDate, newViewDate;
22877 e.preventDefault();
22881 if (!this.keyboardNavigation) {
22884 dir = e.keyCode == 37 ? -1 : 1;
22887 newDate = this.moveYear(this.date, dir);
22888 newViewDate = this.moveYear(this.viewDate, dir);
22889 } else if (e.shiftKey){
22890 newDate = this.moveMonth(this.date, dir);
22891 newViewDate = this.moveMonth(this.viewDate, dir);
22893 newDate = new Date(this.date);
22894 newDate.setUTCDate(this.date.getUTCDate() + dir);
22895 newViewDate = new Date(this.viewDate);
22896 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22898 if (this.dateWithinRange(newDate)){
22899 this.date = newDate;
22900 this.viewDate = newViewDate;
22901 this.setValue(this.formatDate(this.date));
22903 e.preventDefault();
22904 dateChanged = true;
22909 if (!this.keyboardNavigation) {
22912 dir = e.keyCode == 38 ? -1 : 1;
22914 newDate = this.moveYear(this.date, dir);
22915 newViewDate = this.moveYear(this.viewDate, dir);
22916 } else if (e.shiftKey){
22917 newDate = this.moveMonth(this.date, dir);
22918 newViewDate = this.moveMonth(this.viewDate, dir);
22920 newDate = new Date(this.date);
22921 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22922 newViewDate = new Date(this.viewDate);
22923 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22925 if (this.dateWithinRange(newDate)){
22926 this.date = newDate;
22927 this.viewDate = newViewDate;
22928 this.setValue(this.formatDate(this.date));
22930 e.preventDefault();
22931 dateChanged = true;
22935 this.setValue(this.formatDate(this.date));
22937 e.preventDefault();
22940 this.setValue(this.formatDate(this.date));
22954 onClick: function(e)
22956 e.stopPropagation();
22957 e.preventDefault();
22959 var target = e.getTarget();
22961 if(target.nodeName.toLowerCase() === 'i'){
22962 target = Roo.get(target).dom.parentNode;
22965 var nodeName = target.nodeName;
22966 var className = target.className;
22967 var html = target.innerHTML;
22968 //Roo.log(nodeName);
22970 switch(nodeName.toLowerCase()) {
22972 switch(className) {
22978 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22979 switch(this.viewMode){
22981 this.viewDate = this.moveMonth(this.viewDate, dir);
22985 this.viewDate = this.moveYear(this.viewDate, dir);
22991 var date = new Date();
22992 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22994 this.setValue(this.formatDate(this.date));
23001 if (className.indexOf('disabled') < 0) {
23002 if (!this.viewDate) {
23003 this.viewDate = new Date();
23005 this.viewDate.setUTCDate(1);
23006 if (className.indexOf('month') > -1) {
23007 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23009 var year = parseInt(html, 10) || 0;
23010 this.viewDate.setUTCFullYear(year);
23014 if(this.singleMode){
23015 this.setValue(this.formatDate(this.viewDate));
23026 //Roo.log(className);
23027 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23028 var day = parseInt(html, 10) || 1;
23029 var year = (this.viewDate || new Date()).getUTCFullYear(),
23030 month = (this.viewDate || new Date()).getUTCMonth();
23032 if (className.indexOf('old') > -1) {
23039 } else if (className.indexOf('new') > -1) {
23047 //Roo.log([year,month,day]);
23048 this.date = this.UTCDate(year, month, day,0,0,0,0);
23049 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23051 //Roo.log(this.formatDate(this.date));
23052 this.setValue(this.formatDate(this.date));
23059 setStartDate: function(startDate)
23061 this.startDate = startDate || -Infinity;
23062 if (this.startDate !== -Infinity) {
23063 this.startDate = this.parseDate(this.startDate);
23066 this.updateNavArrows();
23069 setEndDate: function(endDate)
23071 this.endDate = endDate || Infinity;
23072 if (this.endDate !== Infinity) {
23073 this.endDate = this.parseDate(this.endDate);
23076 this.updateNavArrows();
23079 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23081 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23082 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23083 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23085 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23086 return parseInt(d, 10);
23089 this.updateNavArrows();
23092 updateNavArrows: function()
23094 if(this.singleMode){
23098 var d = new Date(this.viewDate),
23099 year = d.getUTCFullYear(),
23100 month = d.getUTCMonth();
23102 Roo.each(this.picker().select('.prev', true).elements, function(v){
23104 switch (this.viewMode) {
23107 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23113 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23120 Roo.each(this.picker().select('.next', true).elements, function(v){
23122 switch (this.viewMode) {
23125 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23131 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23139 moveMonth: function(date, dir)
23144 var new_date = new Date(date.valueOf()),
23145 day = new_date.getUTCDate(),
23146 month = new_date.getUTCMonth(),
23147 mag = Math.abs(dir),
23149 dir = dir > 0 ? 1 : -1;
23152 // If going back one month, make sure month is not current month
23153 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23155 return new_date.getUTCMonth() == month;
23157 // If going forward one month, make sure month is as expected
23158 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23160 return new_date.getUTCMonth() != new_month;
23162 new_month = month + dir;
23163 new_date.setUTCMonth(new_month);
23164 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23165 if (new_month < 0 || new_month > 11) {
23166 new_month = (new_month + 12) % 12;
23169 // For magnitudes >1, move one month at a time...
23170 for (var i=0; i<mag; i++) {
23171 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23172 new_date = this.moveMonth(new_date, dir);
23174 // ...then reset the day, keeping it in the new month
23175 new_month = new_date.getUTCMonth();
23176 new_date.setUTCDate(day);
23178 return new_month != new_date.getUTCMonth();
23181 // Common date-resetting loop -- if date is beyond end of month, make it
23184 new_date.setUTCDate(--day);
23185 new_date.setUTCMonth(new_month);
23190 moveYear: function(date, dir)
23192 return this.moveMonth(date, dir*12);
23195 dateWithinRange: function(date)
23197 return date >= this.startDate && date <= this.endDate;
23203 this.picker().remove();
23206 validateValue : function(value)
23208 if(this.getVisibilityEl().hasClass('hidden')){
23212 if(value.length < 1) {
23213 if(this.allowBlank){
23219 if(value.length < this.minLength){
23222 if(value.length > this.maxLength){
23226 var vt = Roo.form.VTypes;
23227 if(!vt[this.vtype](value, this)){
23231 if(typeof this.validator == "function"){
23232 var msg = this.validator(value);
23238 if(this.regex && !this.regex.test(value)){
23242 if(typeof(this.parseDate(value)) == 'undefined'){
23246 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23250 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23260 this.date = this.viewDate = '';
23262 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23267 Roo.apply(Roo.bootstrap.DateField, {
23278 html: '<i class="fa fa-arrow-left"/>'
23288 html: '<i class="fa fa-arrow-right"/>'
23330 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23331 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23332 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23333 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23334 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23347 navFnc: 'FullYear',
23352 navFnc: 'FullYear',
23357 Roo.apply(Roo.bootstrap.DateField, {
23361 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23365 cls: 'datepicker-days',
23369 cls: 'table-condensed',
23371 Roo.bootstrap.DateField.head,
23375 Roo.bootstrap.DateField.footer
23382 cls: 'datepicker-months',
23386 cls: 'table-condensed',
23388 Roo.bootstrap.DateField.head,
23389 Roo.bootstrap.DateField.content,
23390 Roo.bootstrap.DateField.footer
23397 cls: 'datepicker-years',
23401 cls: 'table-condensed',
23403 Roo.bootstrap.DateField.head,
23404 Roo.bootstrap.DateField.content,
23405 Roo.bootstrap.DateField.footer
23424 * @class Roo.bootstrap.TimeField
23425 * @extends Roo.bootstrap.Input
23426 * Bootstrap DateField class
23430 * Create a new TimeField
23431 * @param {Object} config The config object
23434 Roo.bootstrap.TimeField = function(config){
23435 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23439 * Fires when this field show.
23440 * @param {Roo.bootstrap.DateField} thisthis
23441 * @param {Mixed} date The date value
23446 * Fires when this field hide.
23447 * @param {Roo.bootstrap.DateField} this
23448 * @param {Mixed} date The date value
23453 * Fires when select a date.
23454 * @param {Roo.bootstrap.DateField} this
23455 * @param {Mixed} date The date value
23461 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23464 * @cfg {String} format
23465 * The default time format string which can be overriden for localization support. The format must be
23466 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23470 getAutoCreate : function()
23472 this.after = '<i class="fa far fa-clock"></i>';
23473 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23477 onRender: function(ct, position)
23480 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23482 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23484 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23486 this.pop = this.picker().select('>.datepicker-time',true).first();
23487 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23489 this.picker().on('mousedown', this.onMousedown, this);
23490 this.picker().on('click', this.onClick, this);
23492 this.picker().addClass('datepicker-dropdown');
23497 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23498 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23499 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23500 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23501 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23502 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23506 fireKey: function(e){
23507 if (!this.picker().isVisible()){
23508 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23514 e.preventDefault();
23522 this.onTogglePeriod();
23525 this.onIncrementMinutes();
23528 this.onDecrementMinutes();
23537 onClick: function(e) {
23538 e.stopPropagation();
23539 e.preventDefault();
23542 picker : function()
23544 return this.pickerEl;
23547 fillTime: function()
23549 var time = this.pop.select('tbody', true).first();
23551 time.dom.innerHTML = '';
23566 cls: 'hours-up fa fas fa-chevron-up'
23586 cls: 'minutes-up fa fas fa-chevron-up'
23607 cls: 'timepicker-hour',
23622 cls: 'timepicker-minute',
23637 cls: 'btn btn-primary period',
23659 cls: 'hours-down fa fas fa-chevron-down'
23679 cls: 'minutes-down fa fas fa-chevron-down'
23697 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23704 var hours = this.time.getHours();
23705 var minutes = this.time.getMinutes();
23718 hours = hours - 12;
23722 hours = '0' + hours;
23726 minutes = '0' + minutes;
23729 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23730 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23731 this.pop.select('button', true).first().dom.innerHTML = period;
23737 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23739 var cls = ['bottom'];
23741 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23748 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23752 //this.picker().setXY(20000,20000);
23753 this.picker().addClass(cls.join('-'));
23757 Roo.each(cls, function(c){
23762 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23763 //_this.picker().setTop(_this.inputEl().getHeight());
23767 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23769 //_this.picker().setTop(0 - _this.picker().getHeight());
23774 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23778 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23786 onFocus : function()
23788 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23792 onBlur : function()
23794 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23800 this.picker().show();
23805 this.fireEvent('show', this, this.date);
23810 this.picker().hide();
23813 this.fireEvent('hide', this, this.date);
23816 setTime : function()
23819 this.setValue(this.time.format(this.format));
23821 this.fireEvent('select', this, this.date);
23826 onMousedown: function(e){
23827 e.stopPropagation();
23828 e.preventDefault();
23831 onIncrementHours: function()
23833 Roo.log('onIncrementHours');
23834 this.time = this.time.add(Date.HOUR, 1);
23839 onDecrementHours: function()
23841 Roo.log('onDecrementHours');
23842 this.time = this.time.add(Date.HOUR, -1);
23846 onIncrementMinutes: function()
23848 Roo.log('onIncrementMinutes');
23849 this.time = this.time.add(Date.MINUTE, 1);
23853 onDecrementMinutes: function()
23855 Roo.log('onDecrementMinutes');
23856 this.time = this.time.add(Date.MINUTE, -1);
23860 onTogglePeriod: function()
23862 Roo.log('onTogglePeriod');
23863 this.time = this.time.add(Date.HOUR, 12);
23871 Roo.apply(Roo.bootstrap.TimeField, {
23875 cls: 'datepicker dropdown-menu',
23879 cls: 'datepicker-time',
23883 cls: 'table-condensed',
23912 cls: 'btn btn-info ok',
23940 * @class Roo.bootstrap.MonthField
23941 * @extends Roo.bootstrap.Input
23942 * Bootstrap MonthField class
23944 * @cfg {String} language default en
23947 * Create a new MonthField
23948 * @param {Object} config The config object
23951 Roo.bootstrap.MonthField = function(config){
23952 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23957 * Fires when this field show.
23958 * @param {Roo.bootstrap.MonthField} this
23959 * @param {Mixed} date The date value
23964 * Fires when this field hide.
23965 * @param {Roo.bootstrap.MonthField} this
23966 * @param {Mixed} date The date value
23971 * Fires when select a date.
23972 * @param {Roo.bootstrap.MonthField} this
23973 * @param {String} oldvalue The old value
23974 * @param {String} newvalue The new value
23980 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23982 onRender: function(ct, position)
23985 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23987 this.language = this.language || 'en';
23988 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23989 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23991 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23992 this.isInline = false;
23993 this.isInput = true;
23994 this.component = this.el.select('.add-on', true).first() || false;
23995 this.component = (this.component && this.component.length === 0) ? false : this.component;
23996 this.hasInput = this.component && this.inputEL().length;
23998 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24000 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24002 this.picker().on('mousedown', this.onMousedown, this);
24003 this.picker().on('click', this.onClick, this);
24005 this.picker().addClass('datepicker-dropdown');
24007 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24008 v.setStyle('width', '189px');
24015 if(this.isInline) {
24021 setValue: function(v, suppressEvent)
24023 var o = this.getValue();
24025 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24029 if(suppressEvent !== true){
24030 this.fireEvent('select', this, o, v);
24035 getValue: function()
24040 onClick: function(e)
24042 e.stopPropagation();
24043 e.preventDefault();
24045 var target = e.getTarget();
24047 if(target.nodeName.toLowerCase() === 'i'){
24048 target = Roo.get(target).dom.parentNode;
24051 var nodeName = target.nodeName;
24052 var className = target.className;
24053 var html = target.innerHTML;
24055 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24059 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24061 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24067 picker : function()
24069 return this.pickerEl;
24072 fillMonths: function()
24075 var months = this.picker().select('>.datepicker-months td', true).first();
24077 months.dom.innerHTML = '';
24083 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24086 months.createChild(month);
24095 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24096 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24099 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24100 e.removeClass('active');
24102 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24103 e.addClass('active');
24110 if(this.isInline) {
24114 this.picker().removeClass(['bottom', 'top']);
24116 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24118 * place to the top of element!
24122 this.picker().addClass('top');
24123 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24128 this.picker().addClass('bottom');
24130 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24133 onFocus : function()
24135 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24139 onBlur : function()
24141 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24143 var d = this.inputEl().getValue();
24152 this.picker().show();
24153 this.picker().select('>.datepicker-months', true).first().show();
24157 this.fireEvent('show', this, this.date);
24162 if(this.isInline) {
24165 this.picker().hide();
24166 this.fireEvent('hide', this, this.date);
24170 onMousedown: function(e)
24172 e.stopPropagation();
24173 e.preventDefault();
24178 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24182 fireKey: function(e)
24184 if (!this.picker().isVisible()){
24185 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24196 e.preventDefault();
24200 dir = e.keyCode == 37 ? -1 : 1;
24202 this.vIndex = this.vIndex + dir;
24204 if(this.vIndex < 0){
24208 if(this.vIndex > 11){
24212 if(isNaN(this.vIndex)){
24216 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24222 dir = e.keyCode == 38 ? -1 : 1;
24224 this.vIndex = this.vIndex + dir * 4;
24226 if(this.vIndex < 0){
24230 if(this.vIndex > 11){
24234 if(isNaN(this.vIndex)){
24238 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24243 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24244 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24248 e.preventDefault();
24251 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24252 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24268 this.picker().remove();
24273 Roo.apply(Roo.bootstrap.MonthField, {
24292 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24293 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24298 Roo.apply(Roo.bootstrap.MonthField, {
24302 cls: 'datepicker dropdown-menu roo-dynamic',
24306 cls: 'datepicker-months',
24310 cls: 'table-condensed',
24312 Roo.bootstrap.DateField.content
24332 * @class Roo.bootstrap.CheckBox
24333 * @extends Roo.bootstrap.Input
24334 * Bootstrap CheckBox class
24336 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24337 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24338 * @cfg {String} boxLabel The text that appears beside the checkbox
24339 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24340 * @cfg {Boolean} checked initnal the element
24341 * @cfg {Boolean} inline inline the element (default false)
24342 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24343 * @cfg {String} tooltip label tooltip
24346 * Create a new CheckBox
24347 * @param {Object} config The config object
24350 Roo.bootstrap.CheckBox = function(config){
24351 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24356 * Fires when the element is checked or unchecked.
24357 * @param {Roo.bootstrap.CheckBox} this This input
24358 * @param {Boolean} checked The new checked value
24363 * Fires when the element is click.
24364 * @param {Roo.bootstrap.CheckBox} this This input
24371 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24373 inputType: 'checkbox',
24382 // checkbox success does not make any sense really..
24387 getAutoCreate : function()
24389 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24395 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24398 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24404 type : this.inputType,
24405 value : this.inputValue,
24406 cls : 'roo-' + this.inputType, //'form-box',
24407 placeholder : this.placeholder || ''
24411 if(this.inputType != 'radio'){
24415 cls : 'roo-hidden-value',
24416 value : this.checked ? this.inputValue : this.valueOff
24421 if (this.weight) { // Validity check?
24422 cfg.cls += " " + this.inputType + "-" + this.weight;
24425 if (this.disabled) {
24426 input.disabled=true;
24430 input.checked = this.checked;
24435 input.name = this.name;
24437 if(this.inputType != 'radio'){
24438 hidden.name = this.name;
24439 input.name = '_hidden_' + this.name;
24444 input.cls += ' input-' + this.size;
24449 ['xs','sm','md','lg'].map(function(size){
24450 if (settings[size]) {
24451 cfg.cls += ' col-' + size + '-' + settings[size];
24455 var inputblock = input;
24457 if (this.before || this.after) {
24460 cls : 'input-group',
24465 inputblock.cn.push({
24467 cls : 'input-group-addon',
24472 inputblock.cn.push(input);
24474 if(this.inputType != 'radio'){
24475 inputblock.cn.push(hidden);
24479 inputblock.cn.push({
24481 cls : 'input-group-addon',
24487 var boxLabelCfg = false;
24493 //'for': id, // box label is handled by onclick - so no for...
24495 html: this.boxLabel
24498 boxLabelCfg.tooltip = this.tooltip;
24504 if (align ==='left' && this.fieldLabel.length) {
24505 // Roo.log("left and has label");
24510 cls : 'control-label',
24511 html : this.fieldLabel
24522 cfg.cn[1].cn.push(boxLabelCfg);
24525 if(this.labelWidth > 12){
24526 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24529 if(this.labelWidth < 13 && this.labelmd == 0){
24530 this.labelmd = this.labelWidth;
24533 if(this.labellg > 0){
24534 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24535 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24538 if(this.labelmd > 0){
24539 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24540 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24543 if(this.labelsm > 0){
24544 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24545 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24548 if(this.labelxs > 0){
24549 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24550 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24553 } else if ( this.fieldLabel.length) {
24554 // Roo.log(" label");
24558 tag: this.boxLabel ? 'span' : 'label',
24560 cls: 'control-label box-input-label',
24561 //cls : 'input-group-addon',
24562 html : this.fieldLabel
24569 cfg.cn.push(boxLabelCfg);
24574 // Roo.log(" no label && no align");
24575 cfg.cn = [ inputblock ] ;
24577 cfg.cn.push(boxLabelCfg);
24585 if(this.inputType != 'radio'){
24586 cfg.cn.push(hidden);
24594 * return the real input element.
24596 inputEl: function ()
24598 return this.el.select('input.roo-' + this.inputType,true).first();
24600 hiddenEl: function ()
24602 return this.el.select('input.roo-hidden-value',true).first();
24605 labelEl: function()
24607 return this.el.select('label.control-label',true).first();
24609 /* depricated... */
24613 return this.labelEl();
24616 boxLabelEl: function()
24618 return this.el.select('label.box-label',true).first();
24621 initEvents : function()
24623 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24625 this.inputEl().on('click', this.onClick, this);
24627 if (this.boxLabel) {
24628 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24631 this.startValue = this.getValue();
24634 Roo.bootstrap.CheckBox.register(this);
24638 onClick : function(e)
24640 if(this.fireEvent('click', this, e) !== false){
24641 this.setChecked(!this.checked);
24646 setChecked : function(state,suppressEvent)
24648 this.startValue = this.getValue();
24650 if(this.inputType == 'radio'){
24652 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24653 e.dom.checked = false;
24656 this.inputEl().dom.checked = true;
24658 this.inputEl().dom.value = this.inputValue;
24660 if(suppressEvent !== true){
24661 this.fireEvent('check', this, true);
24669 this.checked = state;
24671 this.inputEl().dom.checked = state;
24674 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24676 if(suppressEvent !== true){
24677 this.fireEvent('check', this, state);
24683 getValue : function()
24685 if(this.inputType == 'radio'){
24686 return this.getGroupValue();
24689 return this.hiddenEl().dom.value;
24693 getGroupValue : function()
24695 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24699 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24702 setValue : function(v,suppressEvent)
24704 if(this.inputType == 'radio'){
24705 this.setGroupValue(v, suppressEvent);
24709 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24714 setGroupValue : function(v, suppressEvent)
24716 this.startValue = this.getValue();
24718 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24719 e.dom.checked = false;
24721 if(e.dom.value == v){
24722 e.dom.checked = true;
24726 if(suppressEvent !== true){
24727 this.fireEvent('check', this, true);
24735 validate : function()
24737 if(this.getVisibilityEl().hasClass('hidden')){
24743 (this.inputType == 'radio' && this.validateRadio()) ||
24744 (this.inputType == 'checkbox' && this.validateCheckbox())
24750 this.markInvalid();
24754 validateRadio : function()
24756 if(this.getVisibilityEl().hasClass('hidden')){
24760 if(this.allowBlank){
24766 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24767 if(!e.dom.checked){
24779 validateCheckbox : function()
24782 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24783 //return (this.getValue() == this.inputValue) ? true : false;
24786 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24794 for(var i in group){
24795 if(group[i].el.isVisible(true)){
24803 for(var i in group){
24808 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24815 * Mark this field as valid
24817 markValid : function()
24821 this.fireEvent('valid', this);
24823 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24826 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24833 if(this.inputType == 'radio'){
24834 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24835 var fg = e.findParent('.form-group', false, true);
24836 if (Roo.bootstrap.version == 3) {
24837 fg.removeClass([_this.invalidClass, _this.validClass]);
24838 fg.addClass(_this.validClass);
24840 fg.removeClass(['is-valid', 'is-invalid']);
24841 fg.addClass('is-valid');
24849 var fg = this.el.findParent('.form-group', false, true);
24850 if (Roo.bootstrap.version == 3) {
24851 fg.removeClass([this.invalidClass, this.validClass]);
24852 fg.addClass(this.validClass);
24854 fg.removeClass(['is-valid', 'is-invalid']);
24855 fg.addClass('is-valid');
24860 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24866 for(var i in group){
24867 var fg = group[i].el.findParent('.form-group', false, true);
24868 if (Roo.bootstrap.version == 3) {
24869 fg.removeClass([this.invalidClass, this.validClass]);
24870 fg.addClass(this.validClass);
24872 fg.removeClass(['is-valid', 'is-invalid']);
24873 fg.addClass('is-valid');
24879 * Mark this field as invalid
24880 * @param {String} msg The validation message
24882 markInvalid : function(msg)
24884 if(this.allowBlank){
24890 this.fireEvent('invalid', this, msg);
24892 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24895 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24899 label.markInvalid();
24902 if(this.inputType == 'radio'){
24904 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24905 var fg = e.findParent('.form-group', false, true);
24906 if (Roo.bootstrap.version == 3) {
24907 fg.removeClass([_this.invalidClass, _this.validClass]);
24908 fg.addClass(_this.invalidClass);
24910 fg.removeClass(['is-invalid', 'is-valid']);
24911 fg.addClass('is-invalid');
24919 var fg = this.el.findParent('.form-group', false, true);
24920 if (Roo.bootstrap.version == 3) {
24921 fg.removeClass([_this.invalidClass, _this.validClass]);
24922 fg.addClass(_this.invalidClass);
24924 fg.removeClass(['is-invalid', 'is-valid']);
24925 fg.addClass('is-invalid');
24930 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24936 for(var i in group){
24937 var fg = group[i].el.findParent('.form-group', false, true);
24938 if (Roo.bootstrap.version == 3) {
24939 fg.removeClass([_this.invalidClass, _this.validClass]);
24940 fg.addClass(_this.invalidClass);
24942 fg.removeClass(['is-invalid', 'is-valid']);
24943 fg.addClass('is-invalid');
24949 clearInvalid : function()
24951 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24953 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24955 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24957 if (label && label.iconEl) {
24958 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24959 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24963 disable : function()
24965 if(this.inputType != 'radio'){
24966 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24973 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24974 _this.getActionEl().addClass(this.disabledClass);
24975 e.dom.disabled = true;
24979 this.disabled = true;
24980 this.fireEvent("disable", this);
24984 enable : function()
24986 if(this.inputType != 'radio'){
24987 Roo.bootstrap.CheckBox.superclass.enable.call(this);
24994 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24995 _this.getActionEl().removeClass(this.disabledClass);
24996 e.dom.disabled = false;
25000 this.disabled = false;
25001 this.fireEvent("enable", this);
25005 setBoxLabel : function(v)
25010 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25016 Roo.apply(Roo.bootstrap.CheckBox, {
25021 * register a CheckBox Group
25022 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25024 register : function(checkbox)
25026 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25027 this.groups[checkbox.groupId] = {};
25030 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25034 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25038 * fetch a CheckBox Group based on the group ID
25039 * @param {string} the group ID
25040 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25042 get: function(groupId) {
25043 if (typeof(this.groups[groupId]) == 'undefined') {
25047 return this.groups[groupId] ;
25060 * @class Roo.bootstrap.Radio
25061 * @extends Roo.bootstrap.Component
25062 * Bootstrap Radio class
25063 * @cfg {String} boxLabel - the label associated
25064 * @cfg {String} value - the value of radio
25067 * Create a new Radio
25068 * @param {Object} config The config object
25070 Roo.bootstrap.Radio = function(config){
25071 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25075 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25081 getAutoCreate : function()
25085 cls : 'form-group radio',
25090 html : this.boxLabel
25098 initEvents : function()
25100 this.parent().register(this);
25102 this.el.on('click', this.onClick, this);
25106 onClick : function(e)
25108 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25109 this.setChecked(true);
25113 setChecked : function(state, suppressEvent)
25115 this.parent().setValue(this.value, suppressEvent);
25119 setBoxLabel : function(v)
25124 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25139 * @class Roo.bootstrap.SecurePass
25140 * @extends Roo.bootstrap.Input
25141 * Bootstrap SecurePass class
25145 * Create a new SecurePass
25146 * @param {Object} config The config object
25149 Roo.bootstrap.SecurePass = function (config) {
25150 // these go here, so the translation tool can replace them..
25152 PwdEmpty: "Please type a password, and then retype it to confirm.",
25153 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25154 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25155 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25156 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25157 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25158 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25159 TooWeak: "Your password is Too Weak."
25161 this.meterLabel = "Password strength:";
25162 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25163 this.meterClass = [
25164 "roo-password-meter-tooweak",
25165 "roo-password-meter-weak",
25166 "roo-password-meter-medium",
25167 "roo-password-meter-strong",
25168 "roo-password-meter-grey"
25173 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25176 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25178 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25180 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25181 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25182 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25183 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25184 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25185 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25186 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25196 * @cfg {String/Object} Label for the strength meter (defaults to
25197 * 'Password strength:')
25202 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25203 * ['Weak', 'Medium', 'Strong'])
25206 pwdStrengths: false,
25219 initEvents: function ()
25221 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25223 if (this.el.is('input[type=password]') && Roo.isSafari) {
25224 this.el.on('keydown', this.SafariOnKeyDown, this);
25227 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25230 onRender: function (ct, position)
25232 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25233 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25234 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25236 this.trigger.createChild({
25241 cls: 'roo-password-meter-grey col-xs-12',
25244 //width: this.meterWidth + 'px'
25248 cls: 'roo-password-meter-text'
25254 if (this.hideTrigger) {
25255 this.trigger.setDisplayed(false);
25257 this.setSize(this.width || '', this.height || '');
25260 onDestroy: function ()
25262 if (this.trigger) {
25263 this.trigger.removeAllListeners();
25264 this.trigger.remove();
25267 this.wrap.remove();
25269 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25272 checkStrength: function ()
25274 var pwd = this.inputEl().getValue();
25275 if (pwd == this._lastPwd) {
25280 if (this.ClientSideStrongPassword(pwd)) {
25282 } else if (this.ClientSideMediumPassword(pwd)) {
25284 } else if (this.ClientSideWeakPassword(pwd)) {
25290 Roo.log('strength1: ' + strength);
25292 //var pm = this.trigger.child('div/div/div').dom;
25293 var pm = this.trigger.child('div/div');
25294 pm.removeClass(this.meterClass);
25295 pm.addClass(this.meterClass[strength]);
25298 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25300 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25302 this._lastPwd = pwd;
25306 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25308 this._lastPwd = '';
25310 var pm = this.trigger.child('div/div');
25311 pm.removeClass(this.meterClass);
25312 pm.addClass('roo-password-meter-grey');
25315 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25318 this.inputEl().dom.type='password';
25321 validateValue: function (value)
25323 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25326 if (value.length == 0) {
25327 if (this.allowBlank) {
25328 this.clearInvalid();
25332 this.markInvalid(this.errors.PwdEmpty);
25333 this.errorMsg = this.errors.PwdEmpty;
25341 if (!value.match(/[\x21-\x7e]+/)) {
25342 this.markInvalid(this.errors.PwdBadChar);
25343 this.errorMsg = this.errors.PwdBadChar;
25346 if (value.length < 6) {
25347 this.markInvalid(this.errors.PwdShort);
25348 this.errorMsg = this.errors.PwdShort;
25351 if (value.length > 16) {
25352 this.markInvalid(this.errors.PwdLong);
25353 this.errorMsg = this.errors.PwdLong;
25357 if (this.ClientSideStrongPassword(value)) {
25359 } else if (this.ClientSideMediumPassword(value)) {
25361 } else if (this.ClientSideWeakPassword(value)) {
25368 if (strength < 2) {
25369 //this.markInvalid(this.errors.TooWeak);
25370 this.errorMsg = this.errors.TooWeak;
25375 console.log('strength2: ' + strength);
25377 //var pm = this.trigger.child('div/div/div').dom;
25379 var pm = this.trigger.child('div/div');
25380 pm.removeClass(this.meterClass);
25381 pm.addClass(this.meterClass[strength]);
25383 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25385 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25387 this.errorMsg = '';
25391 CharacterSetChecks: function (type)
25394 this.fResult = false;
25397 isctype: function (character, type)
25400 case this.kCapitalLetter:
25401 if (character >= 'A' && character <= 'Z') {
25406 case this.kSmallLetter:
25407 if (character >= 'a' && character <= 'z') {
25413 if (character >= '0' && character <= '9') {
25418 case this.kPunctuation:
25419 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25430 IsLongEnough: function (pwd, size)
25432 return !(pwd == null || isNaN(size) || pwd.length < size);
25435 SpansEnoughCharacterSets: function (word, nb)
25437 if (!this.IsLongEnough(word, nb))
25442 var characterSetChecks = new Array(
25443 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25444 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25447 for (var index = 0; index < word.length; ++index) {
25448 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25449 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25450 characterSetChecks[nCharSet].fResult = true;
25457 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25458 if (characterSetChecks[nCharSet].fResult) {
25463 if (nCharSets < nb) {
25469 ClientSideStrongPassword: function (pwd)
25471 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25474 ClientSideMediumPassword: function (pwd)
25476 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25479 ClientSideWeakPassword: function (pwd)
25481 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25484 })//<script type="text/javascript">
25487 * Based Ext JS Library 1.1.1
25488 * Copyright(c) 2006-2007, Ext JS, LLC.
25494 * @class Roo.HtmlEditorCore
25495 * @extends Roo.Component
25496 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25498 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25501 Roo.HtmlEditorCore = function(config){
25504 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25509 * @event initialize
25510 * Fires when the editor is fully initialized (including the iframe)
25511 * @param {Roo.HtmlEditorCore} this
25516 * Fires when the editor is first receives the focus. Any insertion must wait
25517 * until after this event.
25518 * @param {Roo.HtmlEditorCore} this
25522 * @event beforesync
25523 * Fires before the textarea is updated with content from the editor iframe. Return false
25524 * to cancel the sync.
25525 * @param {Roo.HtmlEditorCore} this
25526 * @param {String} html
25530 * @event beforepush
25531 * Fires before the iframe editor is updated with content from the textarea. Return false
25532 * to cancel the push.
25533 * @param {Roo.HtmlEditorCore} this
25534 * @param {String} html
25539 * Fires when the textarea is updated with content from the editor iframe.
25540 * @param {Roo.HtmlEditorCore} this
25541 * @param {String} html
25546 * Fires when the iframe editor is updated with content from the textarea.
25547 * @param {Roo.HtmlEditorCore} this
25548 * @param {String} html
25553 * @event editorevent
25554 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25555 * @param {Roo.HtmlEditorCore} this
25561 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25563 // defaults : white / black...
25564 this.applyBlacklists();
25571 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25575 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25581 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25586 * @cfg {Number} height (in pixels)
25590 * @cfg {Number} width (in pixels)
25595 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25598 stylesheets: false,
25603 // private properties
25604 validationEvent : false,
25606 initialized : false,
25608 sourceEditMode : false,
25609 onFocus : Roo.emptyFn,
25611 hideMode:'offsets',
25615 // blacklist + whitelisted elements..
25622 * Protected method that will not generally be called directly. It
25623 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25624 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25626 getDocMarkup : function(){
25630 // inherit styels from page...??
25631 if (this.stylesheets === false) {
25633 Roo.get(document.head).select('style').each(function(node) {
25634 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25637 Roo.get(document.head).select('link').each(function(node) {
25638 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25641 } else if (!this.stylesheets.length) {
25643 st = '<style type="text/css">' +
25644 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25647 for (var i in this.stylesheets) {
25648 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25653 st += '<style type="text/css">' +
25654 'IMG { cursor: pointer } ' +
25657 var cls = 'roo-htmleditor-body';
25659 if(this.bodyCls.length){
25660 cls += ' ' + this.bodyCls;
25663 return '<html><head>' + st +
25664 //<style type="text/css">' +
25665 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25667 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25671 onRender : function(ct, position)
25674 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25675 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25678 this.el.dom.style.border = '0 none';
25679 this.el.dom.setAttribute('tabIndex', -1);
25680 this.el.addClass('x-hidden hide');
25684 if(Roo.isIE){ // fix IE 1px bogus margin
25685 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25689 this.frameId = Roo.id();
25693 var iframe = this.owner.wrap.createChild({
25695 cls: 'form-control', // bootstrap..
25697 name: this.frameId,
25698 frameBorder : 'no',
25699 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25704 this.iframe = iframe.dom;
25706 this.assignDocWin();
25708 this.doc.designMode = 'on';
25711 this.doc.write(this.getDocMarkup());
25715 var task = { // must defer to wait for browser to be ready
25717 //console.log("run task?" + this.doc.readyState);
25718 this.assignDocWin();
25719 if(this.doc.body || this.doc.readyState == 'complete'){
25721 this.doc.designMode="on";
25725 Roo.TaskMgr.stop(task);
25726 this.initEditor.defer(10, this);
25733 Roo.TaskMgr.start(task);
25738 onResize : function(w, h)
25740 Roo.log('resize: ' +w + ',' + h );
25741 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25745 if(typeof w == 'number'){
25747 this.iframe.style.width = w + 'px';
25749 if(typeof h == 'number'){
25751 this.iframe.style.height = h + 'px';
25753 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25760 * Toggles the editor between standard and source edit mode.
25761 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25763 toggleSourceEdit : function(sourceEditMode){
25765 this.sourceEditMode = sourceEditMode === true;
25767 if(this.sourceEditMode){
25769 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25772 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25773 //this.iframe.className = '';
25776 //this.setSize(this.owner.wrap.getSize());
25777 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25784 * Protected method that will not generally be called directly. If you need/want
25785 * custom HTML cleanup, this is the method you should override.
25786 * @param {String} html The HTML to be cleaned
25787 * return {String} The cleaned HTML
25789 cleanHtml : function(html){
25790 html = String(html);
25791 if(html.length > 5){
25792 if(Roo.isSafari){ // strip safari nonsense
25793 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25796 if(html == ' '){
25803 * HTML Editor -> Textarea
25804 * Protected method that will not generally be called directly. Syncs the contents
25805 * of the editor iframe with the textarea.
25807 syncValue : function(){
25808 if(this.initialized){
25809 var bd = (this.doc.body || this.doc.documentElement);
25810 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25811 var html = bd.innerHTML;
25813 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25814 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25816 html = '<div style="'+m[0]+'">' + html + '</div>';
25819 html = this.cleanHtml(html);
25820 // fix up the special chars.. normaly like back quotes in word...
25821 // however we do not want to do this with chinese..
25822 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25824 var cc = match.charCodeAt();
25826 // Get the character value, handling surrogate pairs
25827 if (match.length == 2) {
25828 // It's a surrogate pair, calculate the Unicode code point
25829 var high = match.charCodeAt(0) - 0xD800;
25830 var low = match.charCodeAt(1) - 0xDC00;
25831 cc = (high * 0x400) + low + 0x10000;
25833 (cc >= 0x4E00 && cc < 0xA000 ) ||
25834 (cc >= 0x3400 && cc < 0x4E00 ) ||
25835 (cc >= 0xf900 && cc < 0xfb00 )
25840 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25841 return "&#" + cc + ";";
25848 if(this.owner.fireEvent('beforesync', this, html) !== false){
25849 this.el.dom.value = html;
25850 this.owner.fireEvent('sync', this, html);
25856 * Protected method that will not generally be called directly. Pushes the value of the textarea
25857 * into the iframe editor.
25859 pushValue : function(){
25860 if(this.initialized){
25861 var v = this.el.dom.value.trim();
25863 // if(v.length < 1){
25867 if(this.owner.fireEvent('beforepush', this, v) !== false){
25868 var d = (this.doc.body || this.doc.documentElement);
25870 this.cleanUpPaste();
25871 this.el.dom.value = d.innerHTML;
25872 this.owner.fireEvent('push', this, v);
25878 deferFocus : function(){
25879 this.focus.defer(10, this);
25883 focus : function(){
25884 if(this.win && !this.sourceEditMode){
25891 assignDocWin: function()
25893 var iframe = this.iframe;
25896 this.doc = iframe.contentWindow.document;
25897 this.win = iframe.contentWindow;
25899 // if (!Roo.get(this.frameId)) {
25902 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25903 // this.win = Roo.get(this.frameId).dom.contentWindow;
25905 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25909 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25910 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25915 initEditor : function(){
25916 //console.log("INIT EDITOR");
25917 this.assignDocWin();
25921 this.doc.designMode="on";
25923 this.doc.write(this.getDocMarkup());
25926 var dbody = (this.doc.body || this.doc.documentElement);
25927 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25928 // this copies styles from the containing element into thsi one..
25929 // not sure why we need all of this..
25930 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25932 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25933 //ss['background-attachment'] = 'fixed'; // w3c
25934 dbody.bgProperties = 'fixed'; // ie
25935 //Roo.DomHelper.applyStyles(dbody, ss);
25936 Roo.EventManager.on(this.doc, {
25937 //'mousedown': this.onEditorEvent,
25938 'mouseup': this.onEditorEvent,
25939 'dblclick': this.onEditorEvent,
25940 'click': this.onEditorEvent,
25941 'keyup': this.onEditorEvent,
25946 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25948 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25949 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25951 this.initialized = true;
25953 this.owner.fireEvent('initialize', this);
25958 onDestroy : function(){
25964 //for (var i =0; i < this.toolbars.length;i++) {
25965 // // fixme - ask toolbars for heights?
25966 // this.toolbars[i].onDestroy();
25969 //this.wrap.dom.innerHTML = '';
25970 //this.wrap.remove();
25975 onFirstFocus : function(){
25977 this.assignDocWin();
25980 this.activated = true;
25983 if(Roo.isGecko){ // prevent silly gecko errors
25985 var s = this.win.getSelection();
25986 if(!s.focusNode || s.focusNode.nodeType != 3){
25987 var r = s.getRangeAt(0);
25988 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25993 this.execCmd('useCSS', true);
25994 this.execCmd('styleWithCSS', false);
25997 this.owner.fireEvent('activate', this);
26001 adjustFont: function(btn){
26002 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26003 //if(Roo.isSafari){ // safari
26006 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26007 if(Roo.isSafari){ // safari
26008 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26009 v = (v < 10) ? 10 : v;
26010 v = (v > 48) ? 48 : v;
26011 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26016 v = Math.max(1, v+adjust);
26018 this.execCmd('FontSize', v );
26021 onEditorEvent : function(e)
26023 this.owner.fireEvent('editorevent', this, e);
26024 // this.updateToolbar();
26025 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26028 insertTag : function(tg)
26030 // could be a bit smarter... -> wrap the current selected tRoo..
26031 if (tg.toLowerCase() == 'span' ||
26032 tg.toLowerCase() == 'code' ||
26033 tg.toLowerCase() == 'sup' ||
26034 tg.toLowerCase() == 'sub'
26037 range = this.createRange(this.getSelection());
26038 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26039 wrappingNode.appendChild(range.extractContents());
26040 range.insertNode(wrappingNode);
26047 this.execCmd("formatblock", tg);
26051 insertText : function(txt)
26055 var range = this.createRange();
26056 range.deleteContents();
26057 //alert(Sender.getAttribute('label'));
26059 range.insertNode(this.doc.createTextNode(txt));
26065 * Executes a Midas editor command on the editor document and performs necessary focus and
26066 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26067 * @param {String} cmd The Midas command
26068 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26070 relayCmd : function(cmd, value){
26072 this.execCmd(cmd, value);
26073 this.owner.fireEvent('editorevent', this);
26074 //this.updateToolbar();
26075 this.owner.deferFocus();
26079 * Executes a Midas editor command directly on the editor document.
26080 * For visual commands, you should use {@link #relayCmd} instead.
26081 * <b>This should only be called after the editor is initialized.</b>
26082 * @param {String} cmd The Midas command
26083 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26085 execCmd : function(cmd, value){
26086 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26093 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26095 * @param {String} text | dom node..
26097 insertAtCursor : function(text)
26100 if(!this.activated){
26106 var r = this.doc.selection.createRange();
26117 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26121 // from jquery ui (MIT licenced)
26123 var win = this.win;
26125 if (win.getSelection && win.getSelection().getRangeAt) {
26126 range = win.getSelection().getRangeAt(0);
26127 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26128 range.insertNode(node);
26129 } else if (win.document.selection && win.document.selection.createRange) {
26130 // no firefox support
26131 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26132 win.document.selection.createRange().pasteHTML(txt);
26134 // no firefox support
26135 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26136 this.execCmd('InsertHTML', txt);
26145 mozKeyPress : function(e){
26147 var c = e.getCharCode(), cmd;
26150 c = String.fromCharCode(c).toLowerCase();
26164 this.cleanUpPaste.defer(100, this);
26172 e.preventDefault();
26180 fixKeys : function(){ // load time branching for fastest keydown performance
26182 return function(e){
26183 var k = e.getKey(), r;
26186 r = this.doc.selection.createRange();
26189 r.pasteHTML('    ');
26196 r = this.doc.selection.createRange();
26198 var target = r.parentElement();
26199 if(!target || target.tagName.toLowerCase() != 'li'){
26201 r.pasteHTML('<br />');
26207 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26208 this.cleanUpPaste.defer(100, this);
26214 }else if(Roo.isOpera){
26215 return function(e){
26216 var k = e.getKey();
26220 this.execCmd('InsertHTML','    ');
26223 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26224 this.cleanUpPaste.defer(100, this);
26229 }else if(Roo.isSafari){
26230 return function(e){
26231 var k = e.getKey();
26235 this.execCmd('InsertText','\t');
26239 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26240 this.cleanUpPaste.defer(100, this);
26248 getAllAncestors: function()
26250 var p = this.getSelectedNode();
26253 a.push(p); // push blank onto stack..
26254 p = this.getParentElement();
26258 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26262 a.push(this.doc.body);
26266 lastSelNode : false,
26269 getSelection : function()
26271 this.assignDocWin();
26272 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26275 getSelectedNode: function()
26277 // this may only work on Gecko!!!
26279 // should we cache this!!!!
26284 var range = this.createRange(this.getSelection()).cloneRange();
26287 var parent = range.parentElement();
26289 var testRange = range.duplicate();
26290 testRange.moveToElementText(parent);
26291 if (testRange.inRange(range)) {
26294 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26297 parent = parent.parentElement;
26302 // is ancestor a text element.
26303 var ac = range.commonAncestorContainer;
26304 if (ac.nodeType == 3) {
26305 ac = ac.parentNode;
26308 var ar = ac.childNodes;
26311 var other_nodes = [];
26312 var has_other_nodes = false;
26313 for (var i=0;i<ar.length;i++) {
26314 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26317 // fullly contained node.
26319 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26324 // probably selected..
26325 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26326 other_nodes.push(ar[i]);
26330 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26335 has_other_nodes = true;
26337 if (!nodes.length && other_nodes.length) {
26338 nodes= other_nodes;
26340 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26346 createRange: function(sel)
26348 // this has strange effects when using with
26349 // top toolbar - not sure if it's a great idea.
26350 //this.editor.contentWindow.focus();
26351 if (typeof sel != "undefined") {
26353 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26355 return this.doc.createRange();
26358 return this.doc.createRange();
26361 getParentElement: function()
26364 this.assignDocWin();
26365 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26367 var range = this.createRange(sel);
26370 var p = range.commonAncestorContainer;
26371 while (p.nodeType == 3) { // text node
26382 * Range intersection.. the hard stuff...
26386 * [ -- selected range --- ]
26390 * if end is before start or hits it. fail.
26391 * if start is after end or hits it fail.
26393 * if either hits (but other is outside. - then it's not
26399 // @see http://www.thismuchiknow.co.uk/?p=64.
26400 rangeIntersectsNode : function(range, node)
26402 var nodeRange = node.ownerDocument.createRange();
26404 nodeRange.selectNode(node);
26406 nodeRange.selectNodeContents(node);
26409 var rangeStartRange = range.cloneRange();
26410 rangeStartRange.collapse(true);
26412 var rangeEndRange = range.cloneRange();
26413 rangeEndRange.collapse(false);
26415 var nodeStartRange = nodeRange.cloneRange();
26416 nodeStartRange.collapse(true);
26418 var nodeEndRange = nodeRange.cloneRange();
26419 nodeEndRange.collapse(false);
26421 return rangeStartRange.compareBoundaryPoints(
26422 Range.START_TO_START, nodeEndRange) == -1 &&
26423 rangeEndRange.compareBoundaryPoints(
26424 Range.START_TO_START, nodeStartRange) == 1;
26428 rangeCompareNode : function(range, node)
26430 var nodeRange = node.ownerDocument.createRange();
26432 nodeRange.selectNode(node);
26434 nodeRange.selectNodeContents(node);
26438 range.collapse(true);
26440 nodeRange.collapse(true);
26442 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26443 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26445 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26447 var nodeIsBefore = ss == 1;
26448 var nodeIsAfter = ee == -1;
26450 if (nodeIsBefore && nodeIsAfter) {
26453 if (!nodeIsBefore && nodeIsAfter) {
26454 return 1; //right trailed.
26457 if (nodeIsBefore && !nodeIsAfter) {
26458 return 2; // left trailed.
26464 // private? - in a new class?
26465 cleanUpPaste : function()
26467 // cleans up the whole document..
26468 Roo.log('cleanuppaste');
26470 this.cleanUpChildren(this.doc.body);
26471 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26472 if (clean != this.doc.body.innerHTML) {
26473 this.doc.body.innerHTML = clean;
26478 cleanWordChars : function(input) {// change the chars to hex code
26479 var he = Roo.HtmlEditorCore;
26481 var output = input;
26482 Roo.each(he.swapCodes, function(sw) {
26483 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26485 output = output.replace(swapper, sw[1]);
26492 cleanUpChildren : function (n)
26494 if (!n.childNodes.length) {
26497 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26498 this.cleanUpChild(n.childNodes[i]);
26505 cleanUpChild : function (node)
26508 //console.log(node);
26509 if (node.nodeName == "#text") {
26510 // clean up silly Windows -- stuff?
26513 if (node.nodeName == "#comment") {
26514 node.parentNode.removeChild(node);
26515 // clean up silly Windows -- stuff?
26518 var lcname = node.tagName.toLowerCase();
26519 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26520 // whitelist of tags..
26522 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26524 node.parentNode.removeChild(node);
26529 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26531 // spans with no attributes - just remove them..
26532 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26533 remove_keep_children = true;
26536 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26537 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26539 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26540 // remove_keep_children = true;
26543 if (remove_keep_children) {
26544 this.cleanUpChildren(node);
26545 // inserts everything just before this node...
26546 while (node.childNodes.length) {
26547 var cn = node.childNodes[0];
26548 node.removeChild(cn);
26549 node.parentNode.insertBefore(cn, node);
26551 node.parentNode.removeChild(node);
26555 if (!node.attributes || !node.attributes.length) {
26560 this.cleanUpChildren(node);
26564 function cleanAttr(n,v)
26567 if (v.match(/^\./) || v.match(/^\//)) {
26570 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26573 if (v.match(/^#/)) {
26576 if (v.match(/^\{/)) { // allow template editing.
26579 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26580 node.removeAttribute(n);
26584 var cwhite = this.cwhite;
26585 var cblack = this.cblack;
26587 function cleanStyle(n,v)
26589 if (v.match(/expression/)) { //XSS?? should we even bother..
26590 node.removeAttribute(n);
26594 var parts = v.split(/;/);
26597 Roo.each(parts, function(p) {
26598 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26602 var l = p.split(':').shift().replace(/\s+/g,'');
26603 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26605 if ( cwhite.length && cblack.indexOf(l) > -1) {
26606 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26607 //node.removeAttribute(n);
26611 // only allow 'c whitelisted system attributes'
26612 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26613 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26614 //node.removeAttribute(n);
26624 if (clean.length) {
26625 node.setAttribute(n, clean.join(';'));
26627 node.removeAttribute(n);
26633 for (var i = node.attributes.length-1; i > -1 ; i--) {
26634 var a = node.attributes[i];
26637 if (a.name.toLowerCase().substr(0,2)=='on') {
26638 node.removeAttribute(a.name);
26641 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26642 node.removeAttribute(a.name);
26645 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26646 cleanAttr(a.name,a.value); // fixme..
26649 if (a.name == 'style') {
26650 cleanStyle(a.name,a.value);
26653 /// clean up MS crap..
26654 // tecnically this should be a list of valid class'es..
26657 if (a.name == 'class') {
26658 if (a.value.match(/^Mso/)) {
26659 node.removeAttribute('class');
26662 if (a.value.match(/^body$/)) {
26663 node.removeAttribute('class');
26674 this.cleanUpChildren(node);
26680 * Clean up MS wordisms...
26682 cleanWord : function(node)
26685 this.cleanWord(this.doc.body);
26690 node.nodeName == 'SPAN' &&
26691 !node.hasAttributes() &&
26692 node.childNodes.length == 1 &&
26693 node.firstChild.nodeName == "#text"
26695 var textNode = node.firstChild;
26696 node.removeChild(textNode);
26697 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26698 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26700 node.parentNode.insertBefore(textNode, node);
26701 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26702 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26704 node.parentNode.removeChild(node);
26707 if (node.nodeName == "#text") {
26708 // clean up silly Windows -- stuff?
26711 if (node.nodeName == "#comment") {
26712 node.parentNode.removeChild(node);
26713 // clean up silly Windows -- stuff?
26717 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26718 node.parentNode.removeChild(node);
26721 //Roo.log(node.tagName);
26722 // remove - but keep children..
26723 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26724 //Roo.log('-- removed');
26725 while (node.childNodes.length) {
26726 var cn = node.childNodes[0];
26727 node.removeChild(cn);
26728 node.parentNode.insertBefore(cn, node);
26729 // move node to parent - and clean it..
26730 this.cleanWord(cn);
26732 node.parentNode.removeChild(node);
26733 /// no need to iterate chidlren = it's got none..
26734 //this.iterateChildren(node, this.cleanWord);
26738 if (node.className.length) {
26740 var cn = node.className.split(/\W+/);
26742 Roo.each(cn, function(cls) {
26743 if (cls.match(/Mso[a-zA-Z]+/)) {
26748 node.className = cna.length ? cna.join(' ') : '';
26750 node.removeAttribute("class");
26754 if (node.hasAttribute("lang")) {
26755 node.removeAttribute("lang");
26758 if (node.hasAttribute("style")) {
26760 var styles = node.getAttribute("style").split(";");
26762 Roo.each(styles, function(s) {
26763 if (!s.match(/:/)) {
26766 var kv = s.split(":");
26767 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26770 // what ever is left... we allow.
26773 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26774 if (!nstyle.length) {
26775 node.removeAttribute('style');
26778 this.iterateChildren(node, this.cleanWord);
26784 * iterateChildren of a Node, calling fn each time, using this as the scole..
26785 * @param {DomNode} node node to iterate children of.
26786 * @param {Function} fn method of this class to call on each item.
26788 iterateChildren : function(node, fn)
26790 if (!node.childNodes.length) {
26793 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26794 fn.call(this, node.childNodes[i])
26800 * cleanTableWidths.
26802 * Quite often pasting from word etc.. results in tables with column and widths.
26803 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26806 cleanTableWidths : function(node)
26811 this.cleanTableWidths(this.doc.body);
26816 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26819 Roo.log(node.tagName);
26820 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26821 this.iterateChildren(node, this.cleanTableWidths);
26824 if (node.hasAttribute('width')) {
26825 node.removeAttribute('width');
26829 if (node.hasAttribute("style")) {
26832 var styles = node.getAttribute("style").split(";");
26834 Roo.each(styles, function(s) {
26835 if (!s.match(/:/)) {
26838 var kv = s.split(":");
26839 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26842 // what ever is left... we allow.
26845 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26846 if (!nstyle.length) {
26847 node.removeAttribute('style');
26851 this.iterateChildren(node, this.cleanTableWidths);
26859 domToHTML : function(currentElement, depth, nopadtext) {
26861 depth = depth || 0;
26862 nopadtext = nopadtext || false;
26864 if (!currentElement) {
26865 return this.domToHTML(this.doc.body);
26868 //Roo.log(currentElement);
26870 var allText = false;
26871 var nodeName = currentElement.nodeName;
26872 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26874 if (nodeName == '#text') {
26876 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26881 if (nodeName != 'BODY') {
26884 // Prints the node tagName, such as <A>, <IMG>, etc
26887 for(i = 0; i < currentElement.attributes.length;i++) {
26889 var aname = currentElement.attributes.item(i).name;
26890 if (!currentElement.attributes.item(i).value.length) {
26893 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26896 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26905 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26908 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26913 // Traverse the tree
26915 var currentElementChild = currentElement.childNodes.item(i);
26916 var allText = true;
26917 var innerHTML = '';
26919 while (currentElementChild) {
26920 // Formatting code (indent the tree so it looks nice on the screen)
26921 var nopad = nopadtext;
26922 if (lastnode == 'SPAN') {
26926 if (currentElementChild.nodeName == '#text') {
26927 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26928 toadd = nopadtext ? toadd : toadd.trim();
26929 if (!nopad && toadd.length > 80) {
26930 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26932 innerHTML += toadd;
26935 currentElementChild = currentElement.childNodes.item(i);
26941 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26943 // Recursively traverse the tree structure of the child node
26944 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26945 lastnode = currentElementChild.nodeName;
26947 currentElementChild=currentElement.childNodes.item(i);
26953 // The remaining code is mostly for formatting the tree
26954 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26959 ret+= "</"+tagName+">";
26965 applyBlacklists : function()
26967 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26968 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26972 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26973 if (b.indexOf(tag) > -1) {
26976 this.white.push(tag);
26980 Roo.each(w, function(tag) {
26981 if (b.indexOf(tag) > -1) {
26984 if (this.white.indexOf(tag) > -1) {
26987 this.white.push(tag);
26992 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26993 if (w.indexOf(tag) > -1) {
26996 this.black.push(tag);
27000 Roo.each(b, function(tag) {
27001 if (w.indexOf(tag) > -1) {
27004 if (this.black.indexOf(tag) > -1) {
27007 this.black.push(tag);
27012 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27013 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27017 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27018 if (b.indexOf(tag) > -1) {
27021 this.cwhite.push(tag);
27025 Roo.each(w, function(tag) {
27026 if (b.indexOf(tag) > -1) {
27029 if (this.cwhite.indexOf(tag) > -1) {
27032 this.cwhite.push(tag);
27037 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27038 if (w.indexOf(tag) > -1) {
27041 this.cblack.push(tag);
27045 Roo.each(b, function(tag) {
27046 if (w.indexOf(tag) > -1) {
27049 if (this.cblack.indexOf(tag) > -1) {
27052 this.cblack.push(tag);
27057 setStylesheets : function(stylesheets)
27059 if(typeof(stylesheets) == 'string'){
27060 Roo.get(this.iframe.contentDocument.head).createChild({
27062 rel : 'stylesheet',
27071 Roo.each(stylesheets, function(s) {
27076 Roo.get(_this.iframe.contentDocument.head).createChild({
27078 rel : 'stylesheet',
27087 removeStylesheets : function()
27091 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27096 setStyle : function(style)
27098 Roo.get(this.iframe.contentDocument.head).createChild({
27107 // hide stuff that is not compatible
27121 * @event specialkey
27125 * @cfg {String} fieldClass @hide
27128 * @cfg {String} focusClass @hide
27131 * @cfg {String} autoCreate @hide
27134 * @cfg {String} inputType @hide
27137 * @cfg {String} invalidClass @hide
27140 * @cfg {String} invalidText @hide
27143 * @cfg {String} msgFx @hide
27146 * @cfg {String} validateOnBlur @hide
27150 Roo.HtmlEditorCore.white = [
27151 'area', 'br', 'img', 'input', 'hr', 'wbr',
27153 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27154 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27155 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27156 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27157 'table', 'ul', 'xmp',
27159 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27162 'dir', 'menu', 'ol', 'ul', 'dl',
27168 Roo.HtmlEditorCore.black = [
27169 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27171 'base', 'basefont', 'bgsound', 'blink', 'body',
27172 'frame', 'frameset', 'head', 'html', 'ilayer',
27173 'iframe', 'layer', 'link', 'meta', 'object',
27174 'script', 'style' ,'title', 'xml' // clean later..
27176 Roo.HtmlEditorCore.clean = [
27177 'script', 'style', 'title', 'xml'
27179 Roo.HtmlEditorCore.remove = [
27184 Roo.HtmlEditorCore.ablack = [
27188 Roo.HtmlEditorCore.aclean = [
27189 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27193 Roo.HtmlEditorCore.pwhite= [
27194 'http', 'https', 'mailto'
27197 // white listed style attributes.
27198 Roo.HtmlEditorCore.cwhite= [
27199 // 'text-align', /// default is to allow most things..
27205 // black listed style attributes.
27206 Roo.HtmlEditorCore.cblack= [
27207 // 'font-size' -- this can be set by the project
27211 Roo.HtmlEditorCore.swapCodes =[
27212 [ 8211, "–" ],
27213 [ 8212, "—" ],
27230 * @class Roo.bootstrap.HtmlEditor
27231 * @extends Roo.bootstrap.TextArea
27232 * Bootstrap HtmlEditor class
27235 * Create a new HtmlEditor
27236 * @param {Object} config The config object
27239 Roo.bootstrap.HtmlEditor = function(config){
27240 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27241 if (!this.toolbars) {
27242 this.toolbars = [];
27245 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27248 * @event initialize
27249 * Fires when the editor is fully initialized (including the iframe)
27250 * @param {HtmlEditor} this
27255 * Fires when the editor is first receives the focus. Any insertion must wait
27256 * until after this event.
27257 * @param {HtmlEditor} this
27261 * @event beforesync
27262 * Fires before the textarea is updated with content from the editor iframe. Return false
27263 * to cancel the sync.
27264 * @param {HtmlEditor} this
27265 * @param {String} html
27269 * @event beforepush
27270 * Fires before the iframe editor is updated with content from the textarea. Return false
27271 * to cancel the push.
27272 * @param {HtmlEditor} this
27273 * @param {String} html
27278 * Fires when the textarea is updated with content from the editor iframe.
27279 * @param {HtmlEditor} this
27280 * @param {String} html
27285 * Fires when the iframe editor is updated with content from the textarea.
27286 * @param {HtmlEditor} this
27287 * @param {String} html
27291 * @event editmodechange
27292 * Fires when the editor switches edit modes
27293 * @param {HtmlEditor} this
27294 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27296 editmodechange: true,
27298 * @event editorevent
27299 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27300 * @param {HtmlEditor} this
27304 * @event firstfocus
27305 * Fires when on first focus - needed by toolbars..
27306 * @param {HtmlEditor} this
27311 * Auto save the htmlEditor value as a file into Events
27312 * @param {HtmlEditor} this
27316 * @event savedpreview
27317 * preview the saved version of htmlEditor
27318 * @param {HtmlEditor} this
27325 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27329 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27334 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27339 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27344 * @cfg {Number} height (in pixels)
27348 * @cfg {Number} width (in pixels)
27353 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27356 stylesheets: false,
27361 // private properties
27362 validationEvent : false,
27364 initialized : false,
27367 onFocus : Roo.emptyFn,
27369 hideMode:'offsets',
27371 tbContainer : false,
27375 toolbarContainer :function() {
27376 return this.wrap.select('.x-html-editor-tb',true).first();
27380 * Protected method that will not generally be called directly. It
27381 * is called when the editor creates its toolbar. Override this method if you need to
27382 * add custom toolbar buttons.
27383 * @param {HtmlEditor} editor
27385 createToolbar : function(){
27386 Roo.log('renewing');
27387 Roo.log("create toolbars");
27389 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27390 this.toolbars[0].render(this.toolbarContainer());
27394 // if (!editor.toolbars || !editor.toolbars.length) {
27395 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27398 // for (var i =0 ; i < editor.toolbars.length;i++) {
27399 // editor.toolbars[i] = Roo.factory(
27400 // typeof(editor.toolbars[i]) == 'string' ?
27401 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27402 // Roo.bootstrap.HtmlEditor);
27403 // editor.toolbars[i].init(editor);
27409 onRender : function(ct, position)
27411 // Roo.log("Call onRender: " + this.xtype);
27413 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27415 this.wrap = this.inputEl().wrap({
27416 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27419 this.editorcore.onRender(ct, position);
27421 if (this.resizable) {
27422 this.resizeEl = new Roo.Resizable(this.wrap, {
27426 minHeight : this.height,
27427 height: this.height,
27428 handles : this.resizable,
27431 resize : function(r, w, h) {
27432 _t.onResize(w,h); // -something
27438 this.createToolbar(this);
27441 if(!this.width && this.resizable){
27442 this.setSize(this.wrap.getSize());
27444 if (this.resizeEl) {
27445 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27446 // should trigger onReize..
27452 onResize : function(w, h)
27454 Roo.log('resize: ' +w + ',' + h );
27455 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27459 if(this.inputEl() ){
27460 if(typeof w == 'number'){
27461 var aw = w - this.wrap.getFrameWidth('lr');
27462 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27465 if(typeof h == 'number'){
27466 var tbh = -11; // fixme it needs to tool bar size!
27467 for (var i =0; i < this.toolbars.length;i++) {
27468 // fixme - ask toolbars for heights?
27469 tbh += this.toolbars[i].el.getHeight();
27470 //if (this.toolbars[i].footer) {
27471 // tbh += this.toolbars[i].footer.el.getHeight();
27479 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27480 ah -= 5; // knock a few pixes off for look..
27481 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27485 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27486 this.editorcore.onResize(ew,eh);
27491 * Toggles the editor between standard and source edit mode.
27492 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27494 toggleSourceEdit : function(sourceEditMode)
27496 this.editorcore.toggleSourceEdit(sourceEditMode);
27498 if(this.editorcore.sourceEditMode){
27499 Roo.log('editor - showing textarea');
27502 // Roo.log(this.syncValue());
27504 this.inputEl().removeClass(['hide', 'x-hidden']);
27505 this.inputEl().dom.removeAttribute('tabIndex');
27506 this.inputEl().focus();
27508 Roo.log('editor - hiding textarea');
27510 // Roo.log(this.pushValue());
27513 this.inputEl().addClass(['hide', 'x-hidden']);
27514 this.inputEl().dom.setAttribute('tabIndex', -1);
27515 //this.deferFocus();
27518 if(this.resizable){
27519 this.setSize(this.wrap.getSize());
27522 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27525 // private (for BoxComponent)
27526 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27528 // private (for BoxComponent)
27529 getResizeEl : function(){
27533 // private (for BoxComponent)
27534 getPositionEl : function(){
27539 initEvents : function(){
27540 this.originalValue = this.getValue();
27544 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27547 // markInvalid : Roo.emptyFn,
27549 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27552 // clearInvalid : Roo.emptyFn,
27554 setValue : function(v){
27555 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27556 this.editorcore.pushValue();
27561 deferFocus : function(){
27562 this.focus.defer(10, this);
27566 focus : function(){
27567 this.editorcore.focus();
27573 onDestroy : function(){
27579 for (var i =0; i < this.toolbars.length;i++) {
27580 // fixme - ask toolbars for heights?
27581 this.toolbars[i].onDestroy();
27584 this.wrap.dom.innerHTML = '';
27585 this.wrap.remove();
27590 onFirstFocus : function(){
27591 //Roo.log("onFirstFocus");
27592 this.editorcore.onFirstFocus();
27593 for (var i =0; i < this.toolbars.length;i++) {
27594 this.toolbars[i].onFirstFocus();
27600 syncValue : function()
27602 this.editorcore.syncValue();
27605 pushValue : function()
27607 this.editorcore.pushValue();
27611 // hide stuff that is not compatible
27625 * @event specialkey
27629 * @cfg {String} fieldClass @hide
27632 * @cfg {String} focusClass @hide
27635 * @cfg {String} autoCreate @hide
27638 * @cfg {String} inputType @hide
27642 * @cfg {String} invalidText @hide
27645 * @cfg {String} msgFx @hide
27648 * @cfg {String} validateOnBlur @hide
27657 Roo.namespace('Roo.bootstrap.htmleditor');
27659 * @class Roo.bootstrap.HtmlEditorToolbar1
27665 new Roo.bootstrap.HtmlEditor({
27668 new Roo.bootstrap.HtmlEditorToolbar1({
27669 disable : { fonts: 1 , format: 1, ..., ... , ...],
27675 * @cfg {Object} disable List of elements to disable..
27676 * @cfg {Array} btns List of additional buttons.
27680 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27683 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27686 Roo.apply(this, config);
27688 // default disabled, based on 'good practice'..
27689 this.disable = this.disable || {};
27690 Roo.applyIf(this.disable, {
27693 specialElements : true
27695 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27697 this.editor = config.editor;
27698 this.editorcore = config.editor.editorcore;
27700 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27702 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27703 // dont call parent... till later.
27705 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27710 editorcore : false,
27715 "h1","h2","h3","h4","h5","h6",
27717 "abbr", "acronym", "address", "cite", "samp", "var",
27721 onRender : function(ct, position)
27723 // Roo.log("Call onRender: " + this.xtype);
27725 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27727 this.el.dom.style.marginBottom = '0';
27729 var editorcore = this.editorcore;
27730 var editor= this.editor;
27733 var btn = function(id,cmd , toggle, handler, html){
27735 var event = toggle ? 'toggle' : 'click';
27740 xns: Roo.bootstrap,
27744 enableToggle:toggle !== false,
27746 pressed : toggle ? false : null,
27749 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27750 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27756 // var cb_box = function...
27761 xns: Roo.bootstrap,
27766 xns: Roo.bootstrap,
27770 Roo.each(this.formats, function(f) {
27771 style.menu.items.push({
27773 xns: Roo.bootstrap,
27774 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27779 editorcore.insertTag(this.tagname);
27786 children.push(style);
27788 btn('bold',false,true);
27789 btn('italic',false,true);
27790 btn('align-left', 'justifyleft',true);
27791 btn('align-center', 'justifycenter',true);
27792 btn('align-right' , 'justifyright',true);
27793 btn('link', false, false, function(btn) {
27794 //Roo.log("create link?");
27795 var url = prompt(this.createLinkText, this.defaultLinkValue);
27796 if(url && url != 'http:/'+'/'){
27797 this.editorcore.relayCmd('createlink', url);
27800 btn('list','insertunorderedlist',true);
27801 btn('pencil', false,true, function(btn){
27803 this.toggleSourceEdit(btn.pressed);
27806 if (this.editor.btns.length > 0) {
27807 for (var i = 0; i<this.editor.btns.length; i++) {
27808 children.push(this.editor.btns[i]);
27816 xns: Roo.bootstrap,
27821 xns: Roo.bootstrap,
27826 cog.menu.items.push({
27828 xns: Roo.bootstrap,
27829 html : Clean styles,
27834 editorcore.insertTag(this.tagname);
27843 this.xtype = 'NavSimplebar';
27845 for(var i=0;i< children.length;i++) {
27847 this.buttons.add(this.addxtypeChild(children[i]));
27851 editor.on('editorevent', this.updateToolbar, this);
27853 onBtnClick : function(id)
27855 this.editorcore.relayCmd(id);
27856 this.editorcore.focus();
27860 * Protected method that will not generally be called directly. It triggers
27861 * a toolbar update by reading the markup state of the current selection in the editor.
27863 updateToolbar: function(){
27865 if(!this.editorcore.activated){
27866 this.editor.onFirstFocus(); // is this neeed?
27870 var btns = this.buttons;
27871 var doc = this.editorcore.doc;
27872 btns.get('bold').setActive(doc.queryCommandState('bold'));
27873 btns.get('italic').setActive(doc.queryCommandState('italic'));
27874 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27876 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27877 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27878 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27880 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27881 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27884 var ans = this.editorcore.getAllAncestors();
27885 if (this.formatCombo) {
27888 var store = this.formatCombo.store;
27889 this.formatCombo.setValue("");
27890 for (var i =0; i < ans.length;i++) {
27891 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27893 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27901 // hides menus... - so this cant be on a menu...
27902 Roo.bootstrap.MenuMgr.hideAll();
27904 Roo.bootstrap.MenuMgr.hideAll();
27905 //this.editorsyncValue();
27907 onFirstFocus: function() {
27908 this.buttons.each(function(item){
27912 toggleSourceEdit : function(sourceEditMode){
27915 if(sourceEditMode){
27916 Roo.log("disabling buttons");
27917 this.buttons.each( function(item){
27918 if(item.cmd != 'pencil'){
27924 Roo.log("enabling buttons");
27925 if(this.editorcore.initialized){
27926 this.buttons.each( function(item){
27932 Roo.log("calling toggole on editor");
27933 // tell the editor that it's been pressed..
27934 this.editor.toggleSourceEdit(sourceEditMode);
27948 * @class Roo.bootstrap.Markdown
27949 * @extends Roo.bootstrap.TextArea
27950 * Bootstrap Showdown editable area
27951 * @cfg {string} content
27954 * Create a new Showdown
27957 Roo.bootstrap.Markdown = function(config){
27958 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27962 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27966 initEvents : function()
27969 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27970 this.markdownEl = this.el.createChild({
27971 cls : 'roo-markdown-area'
27973 this.inputEl().addClass('d-none');
27974 if (this.getValue() == '') {
27975 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27978 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27980 this.markdownEl.on('click', this.toggleTextEdit, this);
27981 this.on('blur', this.toggleTextEdit, this);
27982 this.on('specialkey', this.resizeTextArea, this);
27985 toggleTextEdit : function()
27987 var sh = this.markdownEl.getHeight();
27988 this.inputEl().addClass('d-none');
27989 this.markdownEl.addClass('d-none');
27990 if (!this.editing) {
27992 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27993 this.inputEl().removeClass('d-none');
27994 this.inputEl().focus();
27995 this.editing = true;
27998 // show showdown...
27999 this.updateMarkdown();
28000 this.markdownEl.removeClass('d-none');
28001 this.editing = false;
28004 updateMarkdown : function()
28006 if (this.getValue() == '') {
28007 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28011 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28014 resizeTextArea: function () {
28017 Roo.log([sh, this.getValue().split("\n").length * 30]);
28018 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28020 setValue : function(val)
28022 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28023 if (!this.editing) {
28024 this.updateMarkdown();
28030 if (!this.editing) {
28031 this.toggleTextEdit();
28039 * Ext JS Library 1.1.1
28040 * Copyright(c) 2006-2007, Ext JS, LLC.
28042 * Originally Released Under LGPL - original licence link has changed is not relivant.
28045 * <script type="text/javascript">
28049 * @class Roo.bootstrap.PagingToolbar
28050 * @extends Roo.bootstrap.NavSimplebar
28051 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28053 * Create a new PagingToolbar
28054 * @param {Object} config The config object
28055 * @param {Roo.data.Store} store
28057 Roo.bootstrap.PagingToolbar = function(config)
28059 // old args format still supported... - xtype is prefered..
28060 // created from xtype...
28062 this.ds = config.dataSource;
28064 if (config.store && !this.ds) {
28065 this.store= Roo.factory(config.store, Roo.data);
28066 this.ds = this.store;
28067 this.ds.xmodule = this.xmodule || false;
28070 this.toolbarItems = [];
28071 if (config.items) {
28072 this.toolbarItems = config.items;
28075 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28080 this.bind(this.ds);
28083 if (Roo.bootstrap.version == 4) {
28084 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28086 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28091 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28093 * @cfg {Roo.data.Store} dataSource
28094 * The underlying data store providing the paged data
28097 * @cfg {String/HTMLElement/Element} container
28098 * container The id or element that will contain the toolbar
28101 * @cfg {Boolean} displayInfo
28102 * True to display the displayMsg (defaults to false)
28105 * @cfg {Number} pageSize
28106 * The number of records to display per page (defaults to 20)
28110 * @cfg {String} displayMsg
28111 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28113 displayMsg : 'Displaying {0} - {1} of {2}',
28115 * @cfg {String} emptyMsg
28116 * The message to display when no records are found (defaults to "No data to display")
28118 emptyMsg : 'No data to display',
28120 * Customizable piece of the default paging text (defaults to "Page")
28123 beforePageText : "Page",
28125 * Customizable piece of the default paging text (defaults to "of %0")
28128 afterPageText : "of {0}",
28130 * Customizable piece of the default paging text (defaults to "First Page")
28133 firstText : "First Page",
28135 * Customizable piece of the default paging text (defaults to "Previous Page")
28138 prevText : "Previous Page",
28140 * Customizable piece of the default paging text (defaults to "Next Page")
28143 nextText : "Next Page",
28145 * Customizable piece of the default paging text (defaults to "Last Page")
28148 lastText : "Last Page",
28150 * Customizable piece of the default paging text (defaults to "Refresh")
28153 refreshText : "Refresh",
28157 onRender : function(ct, position)
28159 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28160 this.navgroup.parentId = this.id;
28161 this.navgroup.onRender(this.el, null);
28162 // add the buttons to the navgroup
28164 if(this.displayInfo){
28165 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28166 this.displayEl = this.el.select('.x-paging-info', true).first();
28167 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28168 // this.displayEl = navel.el.select('span',true).first();
28174 Roo.each(_this.buttons, function(e){ // this might need to use render????
28175 Roo.factory(e).render(_this.el);
28179 Roo.each(_this.toolbarItems, function(e) {
28180 _this.navgroup.addItem(e);
28184 this.first = this.navgroup.addItem({
28185 tooltip: this.firstText,
28186 cls: "prev btn-outline-secondary",
28187 html : ' <i class="fa fa-step-backward"></i>',
28189 preventDefault: true,
28190 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28193 this.prev = this.navgroup.addItem({
28194 tooltip: this.prevText,
28195 cls: "prev btn-outline-secondary",
28196 html : ' <i class="fa fa-backward"></i>',
28198 preventDefault: true,
28199 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28201 //this.addSeparator();
28204 var field = this.navgroup.addItem( {
28206 cls : 'x-paging-position btn-outline-secondary',
28208 html : this.beforePageText +
28209 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28210 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28213 this.field = field.el.select('input', true).first();
28214 this.field.on("keydown", this.onPagingKeydown, this);
28215 this.field.on("focus", function(){this.dom.select();});
28218 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28219 //this.field.setHeight(18);
28220 //this.addSeparator();
28221 this.next = this.navgroup.addItem({
28222 tooltip: this.nextText,
28223 cls: "next btn-outline-secondary",
28224 html : ' <i class="fa fa-forward"></i>',
28226 preventDefault: true,
28227 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28229 this.last = this.navgroup.addItem({
28230 tooltip: this.lastText,
28231 html : ' <i class="fa fa-step-forward"></i>',
28232 cls: "next btn-outline-secondary",
28234 preventDefault: true,
28235 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28237 //this.addSeparator();
28238 this.loading = this.navgroup.addItem({
28239 tooltip: this.refreshText,
28240 cls: "btn-outline-secondary",
28241 html : ' <i class="fa fa-refresh"></i>',
28242 preventDefault: true,
28243 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28249 updateInfo : function(){
28250 if(this.displayEl){
28251 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28252 var msg = count == 0 ?
28256 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28258 this.displayEl.update(msg);
28263 onLoad : function(ds, r, o)
28265 this.cursor = o.params && o.params.start ? o.params.start : 0;
28267 var d = this.getPageData(),
28272 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28273 this.field.dom.value = ap;
28274 this.first.setDisabled(ap == 1);
28275 this.prev.setDisabled(ap == 1);
28276 this.next.setDisabled(ap == ps);
28277 this.last.setDisabled(ap == ps);
28278 this.loading.enable();
28283 getPageData : function(){
28284 var total = this.ds.getTotalCount();
28287 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28288 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28293 onLoadError : function(){
28294 this.loading.enable();
28298 onPagingKeydown : function(e){
28299 var k = e.getKey();
28300 var d = this.getPageData();
28302 var v = this.field.dom.value, pageNum;
28303 if(!v || isNaN(pageNum = parseInt(v, 10))){
28304 this.field.dom.value = d.activePage;
28307 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28308 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28311 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))
28313 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28314 this.field.dom.value = pageNum;
28315 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28318 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28320 var v = this.field.dom.value, pageNum;
28321 var increment = (e.shiftKey) ? 10 : 1;
28322 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28325 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28326 this.field.dom.value = d.activePage;
28329 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28331 this.field.dom.value = parseInt(v, 10) + increment;
28332 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28333 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28340 beforeLoad : function(){
28342 this.loading.disable();
28347 onClick : function(which){
28356 ds.load({params:{start: 0, limit: this.pageSize}});
28359 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28362 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28365 var total = ds.getTotalCount();
28366 var extra = total % this.pageSize;
28367 var lastStart = extra ? (total - extra) : total-this.pageSize;
28368 ds.load({params:{start: lastStart, limit: this.pageSize}});
28371 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28377 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28378 * @param {Roo.data.Store} store The data store to unbind
28380 unbind : function(ds){
28381 ds.un("beforeload", this.beforeLoad, this);
28382 ds.un("load", this.onLoad, this);
28383 ds.un("loadexception", this.onLoadError, this);
28384 ds.un("remove", this.updateInfo, this);
28385 ds.un("add", this.updateInfo, this);
28386 this.ds = undefined;
28390 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28391 * @param {Roo.data.Store} store The data store to bind
28393 bind : function(ds){
28394 ds.on("beforeload", this.beforeLoad, this);
28395 ds.on("load", this.onLoad, this);
28396 ds.on("loadexception", this.onLoadError, this);
28397 ds.on("remove", this.updateInfo, this);
28398 ds.on("add", this.updateInfo, this);
28409 * @class Roo.bootstrap.MessageBar
28410 * @extends Roo.bootstrap.Component
28411 * Bootstrap MessageBar class
28412 * @cfg {String} html contents of the MessageBar
28413 * @cfg {String} weight (info | success | warning | danger) default info
28414 * @cfg {String} beforeClass insert the bar before the given class
28415 * @cfg {Boolean} closable (true | false) default false
28416 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28419 * Create a new Element
28420 * @param {Object} config The config object
28423 Roo.bootstrap.MessageBar = function(config){
28424 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28427 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28433 beforeClass: 'bootstrap-sticky-wrap',
28435 getAutoCreate : function(){
28439 cls: 'alert alert-dismissable alert-' + this.weight,
28444 html: this.html || ''
28450 cfg.cls += ' alert-messages-fixed';
28464 onRender : function(ct, position)
28466 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28469 var cfg = Roo.apply({}, this.getAutoCreate());
28473 cfg.cls += ' ' + this.cls;
28476 cfg.style = this.style;
28478 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28480 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28483 this.el.select('>button.close').on('click', this.hide, this);
28489 if (!this.rendered) {
28495 this.fireEvent('show', this);
28501 if (!this.rendered) {
28507 this.fireEvent('hide', this);
28510 update : function()
28512 // var e = this.el.dom.firstChild;
28514 // if(this.closable){
28515 // e = e.nextSibling;
28518 // e.data = this.html || '';
28520 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28536 * @class Roo.bootstrap.Graph
28537 * @extends Roo.bootstrap.Component
28538 * Bootstrap Graph class
28542 @cfg {String} graphtype bar | vbar | pie
28543 @cfg {number} g_x coodinator | centre x (pie)
28544 @cfg {number} g_y coodinator | centre y (pie)
28545 @cfg {number} g_r radius (pie)
28546 @cfg {number} g_height height of the chart (respected by all elements in the set)
28547 @cfg {number} g_width width of the chart (respected by all elements in the set)
28548 @cfg {Object} title The title of the chart
28551 -opts (object) options for the chart
28553 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28554 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28556 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.
28557 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28559 o stretch (boolean)
28561 -opts (object) options for the pie
28564 o startAngle (number)
28565 o endAngle (number)
28569 * Create a new Input
28570 * @param {Object} config The config object
28573 Roo.bootstrap.Graph = function(config){
28574 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28580 * The img click event for the img.
28581 * @param {Roo.EventObject} e
28587 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28598 //g_colors: this.colors,
28605 getAutoCreate : function(){
28616 onRender : function(ct,position){
28619 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28621 if (typeof(Raphael) == 'undefined') {
28622 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28626 this.raphael = Raphael(this.el.dom);
28628 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28629 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28630 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28631 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28633 r.text(160, 10, "Single Series Chart").attr(txtattr);
28634 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28635 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28636 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28638 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28639 r.barchart(330, 10, 300, 220, data1);
28640 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28641 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28644 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28645 // r.barchart(30, 30, 560, 250, xdata, {
28646 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28647 // axis : "0 0 1 1",
28648 // axisxlabels : xdata
28649 // //yvalues : cols,
28652 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28654 // this.load(null,xdata,{
28655 // axis : "0 0 1 1",
28656 // axisxlabels : xdata
28661 load : function(graphtype,xdata,opts)
28663 this.raphael.clear();
28665 graphtype = this.graphtype;
28670 var r = this.raphael,
28671 fin = function () {
28672 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28674 fout = function () {
28675 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28677 pfin = function() {
28678 this.sector.stop();
28679 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28682 this.label[0].stop();
28683 this.label[0].attr({ r: 7.5 });
28684 this.label[1].attr({ "font-weight": 800 });
28687 pfout = function() {
28688 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28691 this.label[0].animate({ r: 5 }, 500, "bounce");
28692 this.label[1].attr({ "font-weight": 400 });
28698 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28701 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28704 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28705 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28707 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28714 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28719 setTitle: function(o)
28724 initEvents: function() {
28727 this.el.on('click', this.onClick, this);
28731 onClick : function(e)
28733 Roo.log('img onclick');
28734 this.fireEvent('click', this, e);
28746 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28749 * @class Roo.bootstrap.dash.NumberBox
28750 * @extends Roo.bootstrap.Component
28751 * Bootstrap NumberBox class
28752 * @cfg {String} headline Box headline
28753 * @cfg {String} content Box content
28754 * @cfg {String} icon Box icon
28755 * @cfg {String} footer Footer text
28756 * @cfg {String} fhref Footer href
28759 * Create a new NumberBox
28760 * @param {Object} config The config object
28764 Roo.bootstrap.dash.NumberBox = function(config){
28765 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28769 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28778 getAutoCreate : function(){
28782 cls : 'small-box ',
28790 cls : 'roo-headline',
28791 html : this.headline
28795 cls : 'roo-content',
28796 html : this.content
28810 cls : 'ion ' + this.icon
28819 cls : 'small-box-footer',
28820 href : this.fhref || '#',
28824 cfg.cn.push(footer);
28831 onRender : function(ct,position){
28832 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28839 setHeadline: function (value)
28841 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28844 setFooter: function (value, href)
28846 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28849 this.el.select('a.small-box-footer',true).first().attr('href', href);
28854 setContent: function (value)
28856 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28859 initEvents: function()
28873 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28876 * @class Roo.bootstrap.dash.TabBox
28877 * @extends Roo.bootstrap.Component
28878 * Bootstrap TabBox class
28879 * @cfg {String} title Title of the TabBox
28880 * @cfg {String} icon Icon of the TabBox
28881 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28882 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28885 * Create a new TabBox
28886 * @param {Object} config The config object
28890 Roo.bootstrap.dash.TabBox = function(config){
28891 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28896 * When a pane is added
28897 * @param {Roo.bootstrap.dash.TabPane} pane
28901 * @event activatepane
28902 * When a pane is activated
28903 * @param {Roo.bootstrap.dash.TabPane} pane
28905 "activatepane" : true
28913 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28918 tabScrollable : false,
28920 getChildContainer : function()
28922 return this.el.select('.tab-content', true).first();
28925 getAutoCreate : function(){
28929 cls: 'pull-left header',
28937 cls: 'fa ' + this.icon
28943 cls: 'nav nav-tabs pull-right',
28949 if(this.tabScrollable){
28956 cls: 'nav nav-tabs pull-right',
28967 cls: 'nav-tabs-custom',
28972 cls: 'tab-content no-padding',
28980 initEvents : function()
28982 //Roo.log('add add pane handler');
28983 this.on('addpane', this.onAddPane, this);
28986 * Updates the box title
28987 * @param {String} html to set the title to.
28989 setTitle : function(value)
28991 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28993 onAddPane : function(pane)
28995 this.panes.push(pane);
28996 //Roo.log('addpane');
28998 // tabs are rendere left to right..
28999 if(!this.showtabs){
29003 var ctr = this.el.select('.nav-tabs', true).first();
29006 var existing = ctr.select('.nav-tab',true);
29007 var qty = existing.getCount();;
29010 var tab = ctr.createChild({
29012 cls : 'nav-tab' + (qty ? '' : ' active'),
29020 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29023 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29025 pane.el.addClass('active');
29030 onTabClick : function(ev,un,ob,pane)
29032 //Roo.log('tab - prev default');
29033 ev.preventDefault();
29036 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29037 pane.tab.addClass('active');
29038 //Roo.log(pane.title);
29039 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29040 // technically we should have a deactivate event.. but maybe add later.
29041 // and it should not de-activate the selected tab...
29042 this.fireEvent('activatepane', pane);
29043 pane.el.addClass('active');
29044 pane.fireEvent('activate');
29049 getActivePane : function()
29052 Roo.each(this.panes, function(p) {
29053 if(p.el.hasClass('active')){
29074 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29076 * @class Roo.bootstrap.TabPane
29077 * @extends Roo.bootstrap.Component
29078 * Bootstrap TabPane class
29079 * @cfg {Boolean} active (false | true) Default false
29080 * @cfg {String} title title of panel
29084 * Create a new TabPane
29085 * @param {Object} config The config object
29088 Roo.bootstrap.dash.TabPane = function(config){
29089 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29095 * When a pane is activated
29096 * @param {Roo.bootstrap.dash.TabPane} pane
29103 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29108 // the tabBox that this is attached to.
29111 getAutoCreate : function()
29119 cfg.cls += ' active';
29124 initEvents : function()
29126 //Roo.log('trigger add pane handler');
29127 this.parent().fireEvent('addpane', this)
29131 * Updates the tab title
29132 * @param {String} html to set the title to.
29134 setTitle: function(str)
29140 this.tab.select('a', true).first().dom.innerHTML = str;
29157 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29160 * @class Roo.bootstrap.menu.Menu
29161 * @extends Roo.bootstrap.Component
29162 * Bootstrap Menu class - container for Menu
29163 * @cfg {String} html Text of the menu
29164 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29165 * @cfg {String} icon Font awesome icon
29166 * @cfg {String} pos Menu align to (top | bottom) default bottom
29170 * Create a new Menu
29171 * @param {Object} config The config object
29175 Roo.bootstrap.menu.Menu = function(config){
29176 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29180 * @event beforeshow
29181 * Fires before this menu is displayed
29182 * @param {Roo.bootstrap.menu.Menu} this
29186 * @event beforehide
29187 * Fires before this menu is hidden
29188 * @param {Roo.bootstrap.menu.Menu} this
29193 * Fires after this menu is displayed
29194 * @param {Roo.bootstrap.menu.Menu} this
29199 * Fires after this menu is hidden
29200 * @param {Roo.bootstrap.menu.Menu} this
29205 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29206 * @param {Roo.bootstrap.menu.Menu} this
29207 * @param {Roo.EventObject} e
29214 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29218 weight : 'default',
29223 getChildContainer : function() {
29224 if(this.isSubMenu){
29228 return this.el.select('ul.dropdown-menu', true).first();
29231 getAutoCreate : function()
29236 cls : 'roo-menu-text',
29244 cls : 'fa ' + this.icon
29255 cls : 'dropdown-button btn btn-' + this.weight,
29260 cls : 'dropdown-toggle btn btn-' + this.weight,
29270 cls : 'dropdown-menu'
29276 if(this.pos == 'top'){
29277 cfg.cls += ' dropup';
29280 if(this.isSubMenu){
29283 cls : 'dropdown-menu'
29290 onRender : function(ct, position)
29292 this.isSubMenu = ct.hasClass('dropdown-submenu');
29294 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29297 initEvents : function()
29299 if(this.isSubMenu){
29303 this.hidden = true;
29305 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29306 this.triggerEl.on('click', this.onTriggerPress, this);
29308 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29309 this.buttonEl.on('click', this.onClick, this);
29315 if(this.isSubMenu){
29319 return this.el.select('ul.dropdown-menu', true).first();
29322 onClick : function(e)
29324 this.fireEvent("click", this, e);
29327 onTriggerPress : function(e)
29329 if (this.isVisible()) {
29336 isVisible : function(){
29337 return !this.hidden;
29342 this.fireEvent("beforeshow", this);
29344 this.hidden = false;
29345 this.el.addClass('open');
29347 Roo.get(document).on("mouseup", this.onMouseUp, this);
29349 this.fireEvent("show", this);
29356 this.fireEvent("beforehide", this);
29358 this.hidden = true;
29359 this.el.removeClass('open');
29361 Roo.get(document).un("mouseup", this.onMouseUp);
29363 this.fireEvent("hide", this);
29366 onMouseUp : function()
29380 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29383 * @class Roo.bootstrap.menu.Item
29384 * @extends Roo.bootstrap.Component
29385 * Bootstrap MenuItem class
29386 * @cfg {Boolean} submenu (true | false) default false
29387 * @cfg {String} html text of the item
29388 * @cfg {String} href the link
29389 * @cfg {Boolean} disable (true | false) default false
29390 * @cfg {Boolean} preventDefault (true | false) default true
29391 * @cfg {String} icon Font awesome icon
29392 * @cfg {String} pos Submenu align to (left | right) default right
29396 * Create a new Item
29397 * @param {Object} config The config object
29401 Roo.bootstrap.menu.Item = function(config){
29402 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29406 * Fires when the mouse is hovering over this menu
29407 * @param {Roo.bootstrap.menu.Item} this
29408 * @param {Roo.EventObject} e
29413 * Fires when the mouse exits this menu
29414 * @param {Roo.bootstrap.menu.Item} this
29415 * @param {Roo.EventObject} e
29421 * The raw click event for the entire grid.
29422 * @param {Roo.EventObject} e
29428 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29433 preventDefault: true,
29438 getAutoCreate : function()
29443 cls : 'roo-menu-item-text',
29451 cls : 'fa ' + this.icon
29460 href : this.href || '#',
29467 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29471 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29473 if(this.pos == 'left'){
29474 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29481 initEvents : function()
29483 this.el.on('mouseover', this.onMouseOver, this);
29484 this.el.on('mouseout', this.onMouseOut, this);
29486 this.el.select('a', true).first().on('click', this.onClick, this);
29490 onClick : function(e)
29492 if(this.preventDefault){
29493 e.preventDefault();
29496 this.fireEvent("click", this, e);
29499 onMouseOver : function(e)
29501 if(this.submenu && this.pos == 'left'){
29502 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29505 this.fireEvent("mouseover", this, e);
29508 onMouseOut : function(e)
29510 this.fireEvent("mouseout", this, e);
29522 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29525 * @class Roo.bootstrap.menu.Separator
29526 * @extends Roo.bootstrap.Component
29527 * Bootstrap Separator class
29530 * Create a new Separator
29531 * @param {Object} config The config object
29535 Roo.bootstrap.menu.Separator = function(config){
29536 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29539 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29541 getAutoCreate : function(){
29544 cls: 'dropdown-divider divider'
29562 * @class Roo.bootstrap.Tooltip
29563 * Bootstrap Tooltip class
29564 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29565 * to determine which dom element triggers the tooltip.
29567 * It needs to add support for additional attributes like tooltip-position
29570 * Create a new Toolti
29571 * @param {Object} config The config object
29574 Roo.bootstrap.Tooltip = function(config){
29575 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29577 this.alignment = Roo.bootstrap.Tooltip.alignment;
29579 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29580 this.alignment = config.alignment;
29585 Roo.apply(Roo.bootstrap.Tooltip, {
29587 * @function init initialize tooltip monitoring.
29591 currentTip : false,
29592 currentRegion : false,
29598 Roo.get(document).on('mouseover', this.enter ,this);
29599 Roo.get(document).on('mouseout', this.leave, this);
29602 this.currentTip = new Roo.bootstrap.Tooltip();
29605 enter : function(ev)
29607 var dom = ev.getTarget();
29609 //Roo.log(['enter',dom]);
29610 var el = Roo.fly(dom);
29611 if (this.currentEl) {
29613 //Roo.log(this.currentEl);
29614 //Roo.log(this.currentEl.contains(dom));
29615 if (this.currentEl == el) {
29618 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29624 if (this.currentTip.el) {
29625 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29629 if(!el || el.dom == document){
29635 if (!el.attr('tooltip')) {
29636 pel = el.findParent("[tooltip]");
29638 bindEl = Roo.get(pel);
29644 // you can not look for children, as if el is the body.. then everythign is the child..
29645 if (!pel && !el.attr('tooltip')) { //
29646 if (!el.select("[tooltip]").elements.length) {
29649 // is the mouse over this child...?
29650 bindEl = el.select("[tooltip]").first();
29651 var xy = ev.getXY();
29652 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29653 //Roo.log("not in region.");
29656 //Roo.log("child element over..");
29659 this.currentEl = el;
29660 this.currentTip.bind(bindEl);
29661 this.currentRegion = Roo.lib.Region.getRegion(dom);
29662 this.currentTip.enter();
29665 leave : function(ev)
29667 var dom = ev.getTarget();
29668 //Roo.log(['leave',dom]);
29669 if (!this.currentEl) {
29674 if (dom != this.currentEl.dom) {
29677 var xy = ev.getXY();
29678 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29681 // only activate leave if mouse cursor is outside... bounding box..
29686 if (this.currentTip) {
29687 this.currentTip.leave();
29689 //Roo.log('clear currentEl');
29690 this.currentEl = false;
29695 'left' : ['r-l', [-2,0], 'right'],
29696 'right' : ['l-r', [2,0], 'left'],
29697 'bottom' : ['t-b', [0,2], 'top'],
29698 'top' : [ 'b-t', [0,-2], 'bottom']
29704 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29709 delay : null, // can be { show : 300 , hide: 500}
29713 hoverState : null, //???
29715 placement : 'bottom',
29719 getAutoCreate : function(){
29726 cls : 'tooltip-arrow arrow'
29729 cls : 'tooltip-inner'
29736 bind : function(el)
29741 initEvents : function()
29743 this.arrowEl = this.el.select('.arrow', true).first();
29744 this.innerEl = this.el.select('.tooltip-inner', true).first();
29747 enter : function () {
29749 if (this.timeout != null) {
29750 clearTimeout(this.timeout);
29753 this.hoverState = 'in';
29754 //Roo.log("enter - show");
29755 if (!this.delay || !this.delay.show) {
29760 this.timeout = setTimeout(function () {
29761 if (_t.hoverState == 'in') {
29764 }, this.delay.show);
29768 clearTimeout(this.timeout);
29770 this.hoverState = 'out';
29771 if (!this.delay || !this.delay.hide) {
29777 this.timeout = setTimeout(function () {
29778 //Roo.log("leave - timeout");
29780 if (_t.hoverState == 'out') {
29782 Roo.bootstrap.Tooltip.currentEl = false;
29787 show : function (msg)
29790 this.render(document.body);
29793 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29795 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29797 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29799 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29800 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29802 var placement = typeof this.placement == 'function' ?
29803 this.placement.call(this, this.el, on_el) :
29806 var autoToken = /\s?auto?\s?/i;
29807 var autoPlace = autoToken.test(placement);
29809 placement = placement.replace(autoToken, '') || 'top';
29813 //this.el.setXY([0,0]);
29815 //this.el.dom.style.display='block';
29817 //this.el.appendTo(on_el);
29819 var p = this.getPosition();
29820 var box = this.el.getBox();
29826 var align = this.alignment[placement];
29828 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29830 if(placement == 'top' || placement == 'bottom'){
29832 placement = 'right';
29835 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29836 placement = 'left';
29839 var scroll = Roo.select('body', true).first().getScroll();
29841 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29845 align = this.alignment[placement];
29847 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29851 var elems = document.getElementsByTagName('div');
29852 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29853 for (var i = 0; i < elems.length; i++) {
29854 var zindex = Number.parseInt(
29855 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29858 if (zindex > highest) {
29865 this.el.dom.style.zIndex = highest;
29867 this.el.alignTo(this.bindEl, align[0],align[1]);
29868 //var arrow = this.el.select('.arrow',true).first();
29869 //arrow.set(align[2],
29871 this.el.addClass(placement);
29872 this.el.addClass("bs-tooltip-"+ placement);
29874 this.el.addClass('in fade show');
29876 this.hoverState = null;
29878 if (this.el.hasClass('fade')) {
29893 //this.el.setXY([0,0]);
29894 this.el.removeClass(['show', 'in']);
29910 * @class Roo.bootstrap.LocationPicker
29911 * @extends Roo.bootstrap.Component
29912 * Bootstrap LocationPicker class
29913 * @cfg {Number} latitude Position when init default 0
29914 * @cfg {Number} longitude Position when init default 0
29915 * @cfg {Number} zoom default 15
29916 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29917 * @cfg {Boolean} mapTypeControl default false
29918 * @cfg {Boolean} disableDoubleClickZoom default false
29919 * @cfg {Boolean} scrollwheel default true
29920 * @cfg {Boolean} streetViewControl default false
29921 * @cfg {Number} radius default 0
29922 * @cfg {String} locationName
29923 * @cfg {Boolean} draggable default true
29924 * @cfg {Boolean} enableAutocomplete default false
29925 * @cfg {Boolean} enableReverseGeocode default true
29926 * @cfg {String} markerTitle
29929 * Create a new LocationPicker
29930 * @param {Object} config The config object
29934 Roo.bootstrap.LocationPicker = function(config){
29936 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29941 * Fires when the picker initialized.
29942 * @param {Roo.bootstrap.LocationPicker} this
29943 * @param {Google Location} location
29947 * @event positionchanged
29948 * Fires when the picker position changed.
29949 * @param {Roo.bootstrap.LocationPicker} this
29950 * @param {Google Location} location
29952 positionchanged : true,
29955 * Fires when the map resize.
29956 * @param {Roo.bootstrap.LocationPicker} this
29961 * Fires when the map show.
29962 * @param {Roo.bootstrap.LocationPicker} this
29967 * Fires when the map hide.
29968 * @param {Roo.bootstrap.LocationPicker} this
29973 * Fires when click the map.
29974 * @param {Roo.bootstrap.LocationPicker} this
29975 * @param {Map event} e
29979 * @event mapRightClick
29980 * Fires when right click the map.
29981 * @param {Roo.bootstrap.LocationPicker} this
29982 * @param {Map event} e
29984 mapRightClick : true,
29986 * @event markerClick
29987 * Fires when click the marker.
29988 * @param {Roo.bootstrap.LocationPicker} this
29989 * @param {Map event} e
29991 markerClick : true,
29993 * @event markerRightClick
29994 * Fires when right click the marker.
29995 * @param {Roo.bootstrap.LocationPicker} this
29996 * @param {Map event} e
29998 markerRightClick : true,
30000 * @event OverlayViewDraw
30001 * Fires when OverlayView Draw
30002 * @param {Roo.bootstrap.LocationPicker} this
30004 OverlayViewDraw : true,
30006 * @event OverlayViewOnAdd
30007 * Fires when OverlayView Draw
30008 * @param {Roo.bootstrap.LocationPicker} this
30010 OverlayViewOnAdd : true,
30012 * @event OverlayViewOnRemove
30013 * Fires when OverlayView Draw
30014 * @param {Roo.bootstrap.LocationPicker} this
30016 OverlayViewOnRemove : true,
30018 * @event OverlayViewShow
30019 * Fires when OverlayView Draw
30020 * @param {Roo.bootstrap.LocationPicker} this
30021 * @param {Pixel} cpx
30023 OverlayViewShow : true,
30025 * @event OverlayViewHide
30026 * Fires when OverlayView Draw
30027 * @param {Roo.bootstrap.LocationPicker} this
30029 OverlayViewHide : true,
30031 * @event loadexception
30032 * Fires when load google lib failed.
30033 * @param {Roo.bootstrap.LocationPicker} this
30035 loadexception : true
30040 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30042 gMapContext: false,
30048 mapTypeControl: false,
30049 disableDoubleClickZoom: false,
30051 streetViewControl: false,
30055 enableAutocomplete: false,
30056 enableReverseGeocode: true,
30059 getAutoCreate: function()
30064 cls: 'roo-location-picker'
30070 initEvents: function(ct, position)
30072 if(!this.el.getWidth() || this.isApplied()){
30076 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30081 initial: function()
30083 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30084 this.fireEvent('loadexception', this);
30088 if(!this.mapTypeId){
30089 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30092 this.gMapContext = this.GMapContext();
30094 this.initOverlayView();
30096 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30100 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30101 _this.setPosition(_this.gMapContext.marker.position);
30104 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30105 _this.fireEvent('mapClick', this, event);
30109 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30110 _this.fireEvent('mapRightClick', this, event);
30114 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30115 _this.fireEvent('markerClick', this, event);
30119 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30120 _this.fireEvent('markerRightClick', this, event);
30124 this.setPosition(this.gMapContext.location);
30126 this.fireEvent('initial', this, this.gMapContext.location);
30129 initOverlayView: function()
30133 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30137 _this.fireEvent('OverlayViewDraw', _this);
30142 _this.fireEvent('OverlayViewOnAdd', _this);
30145 onRemove: function()
30147 _this.fireEvent('OverlayViewOnRemove', _this);
30150 show: function(cpx)
30152 _this.fireEvent('OverlayViewShow', _this, cpx);
30157 _this.fireEvent('OverlayViewHide', _this);
30163 fromLatLngToContainerPixel: function(event)
30165 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30168 isApplied: function()
30170 return this.getGmapContext() == false ? false : true;
30173 getGmapContext: function()
30175 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30178 GMapContext: function()
30180 var position = new google.maps.LatLng(this.latitude, this.longitude);
30182 var _map = new google.maps.Map(this.el.dom, {
30185 mapTypeId: this.mapTypeId,
30186 mapTypeControl: this.mapTypeControl,
30187 disableDoubleClickZoom: this.disableDoubleClickZoom,
30188 scrollwheel: this.scrollwheel,
30189 streetViewControl: this.streetViewControl,
30190 locationName: this.locationName,
30191 draggable: this.draggable,
30192 enableAutocomplete: this.enableAutocomplete,
30193 enableReverseGeocode: this.enableReverseGeocode
30196 var _marker = new google.maps.Marker({
30197 position: position,
30199 title: this.markerTitle,
30200 draggable: this.draggable
30207 location: position,
30208 radius: this.radius,
30209 locationName: this.locationName,
30210 addressComponents: {
30211 formatted_address: null,
30212 addressLine1: null,
30213 addressLine2: null,
30215 streetNumber: null,
30219 stateOrProvince: null
30222 domContainer: this.el.dom,
30223 geodecoder: new google.maps.Geocoder()
30227 drawCircle: function(center, radius, options)
30229 if (this.gMapContext.circle != null) {
30230 this.gMapContext.circle.setMap(null);
30234 options = Roo.apply({}, options, {
30235 strokeColor: "#0000FF",
30236 strokeOpacity: .35,
30238 fillColor: "#0000FF",
30242 options.map = this.gMapContext.map;
30243 options.radius = radius;
30244 options.center = center;
30245 this.gMapContext.circle = new google.maps.Circle(options);
30246 return this.gMapContext.circle;
30252 setPosition: function(location)
30254 this.gMapContext.location = location;
30255 this.gMapContext.marker.setPosition(location);
30256 this.gMapContext.map.panTo(location);
30257 this.drawCircle(location, this.gMapContext.radius, {});
30261 if (this.gMapContext.settings.enableReverseGeocode) {
30262 this.gMapContext.geodecoder.geocode({
30263 latLng: this.gMapContext.location
30264 }, function(results, status) {
30266 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30267 _this.gMapContext.locationName = results[0].formatted_address;
30268 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30270 _this.fireEvent('positionchanged', this, location);
30277 this.fireEvent('positionchanged', this, location);
30282 google.maps.event.trigger(this.gMapContext.map, "resize");
30284 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30286 this.fireEvent('resize', this);
30289 setPositionByLatLng: function(latitude, longitude)
30291 this.setPosition(new google.maps.LatLng(latitude, longitude));
30294 getCurrentPosition: function()
30297 latitude: this.gMapContext.location.lat(),
30298 longitude: this.gMapContext.location.lng()
30302 getAddressName: function()
30304 return this.gMapContext.locationName;
30307 getAddressComponents: function()
30309 return this.gMapContext.addressComponents;
30312 address_component_from_google_geocode: function(address_components)
30316 for (var i = 0; i < address_components.length; i++) {
30317 var component = address_components[i];
30318 if (component.types.indexOf("postal_code") >= 0) {
30319 result.postalCode = component.short_name;
30320 } else if (component.types.indexOf("street_number") >= 0) {
30321 result.streetNumber = component.short_name;
30322 } else if (component.types.indexOf("route") >= 0) {
30323 result.streetName = component.short_name;
30324 } else if (component.types.indexOf("neighborhood") >= 0) {
30325 result.city = component.short_name;
30326 } else if (component.types.indexOf("locality") >= 0) {
30327 result.city = component.short_name;
30328 } else if (component.types.indexOf("sublocality") >= 0) {
30329 result.district = component.short_name;
30330 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30331 result.stateOrProvince = component.short_name;
30332 } else if (component.types.indexOf("country") >= 0) {
30333 result.country = component.short_name;
30337 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30338 result.addressLine2 = "";
30342 setZoomLevel: function(zoom)
30344 this.gMapContext.map.setZoom(zoom);
30357 this.fireEvent('show', this);
30368 this.fireEvent('hide', this);
30373 Roo.apply(Roo.bootstrap.LocationPicker, {
30375 OverlayView : function(map, options)
30377 options = options || {};
30384 * @class Roo.bootstrap.Alert
30385 * @extends Roo.bootstrap.Component
30386 * Bootstrap Alert class - shows an alert area box
30388 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30389 Enter a valid email address
30392 * @cfg {String} title The title of alert
30393 * @cfg {String} html The content of alert
30394 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30395 * @cfg {String} fa font-awesomeicon
30396 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30397 * @cfg {Boolean} close true to show a x closer
30401 * Create a new alert
30402 * @param {Object} config The config object
30406 Roo.bootstrap.Alert = function(config){
30407 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30411 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30417 faicon: false, // BC
30421 getAutoCreate : function()
30433 style : this.close ? '' : 'display:none'
30437 cls : 'roo-alert-icon'
30442 cls : 'roo-alert-title',
30447 cls : 'roo-alert-text',
30454 cfg.cn[0].cls += ' fa ' + this.faicon;
30457 cfg.cn[0].cls += ' fa ' + this.fa;
30461 cfg.cls += ' alert-' + this.weight;
30467 initEvents: function()
30469 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30470 this.titleEl = this.el.select('.roo-alert-title',true).first();
30471 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30472 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30473 if (this.seconds > 0) {
30474 this.hide.defer(this.seconds, this);
30478 * Set the Title Message HTML
30479 * @param {String} html
30481 setTitle : function(str)
30483 this.titleEl.dom.innerHTML = str;
30487 * Set the Body Message HTML
30488 * @param {String} html
30490 setHtml : function(str)
30492 this.htmlEl.dom.innerHTML = str;
30495 * Set the Weight of the alert
30496 * @param {String} (success|info|warning|danger) weight
30499 setWeight : function(weight)
30502 this.el.removeClass('alert-' + this.weight);
30505 this.weight = weight;
30507 this.el.addClass('alert-' + this.weight);
30510 * Set the Icon of the alert
30511 * @param {String} see fontawsome names (name without the 'fa-' bit)
30513 setIcon : function(icon)
30516 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30519 this.faicon = icon;
30521 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30546 * @class Roo.bootstrap.UploadCropbox
30547 * @extends Roo.bootstrap.Component
30548 * Bootstrap UploadCropbox class
30549 * @cfg {String} emptyText show when image has been loaded
30550 * @cfg {String} rotateNotify show when image too small to rotate
30551 * @cfg {Number} errorTimeout default 3000
30552 * @cfg {Number} minWidth default 300
30553 * @cfg {Number} minHeight default 300
30554 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30555 * @cfg {Boolean} isDocument (true|false) default false
30556 * @cfg {String} url action url
30557 * @cfg {String} paramName default 'imageUpload'
30558 * @cfg {String} method default POST
30559 * @cfg {Boolean} loadMask (true|false) default true
30560 * @cfg {Boolean} loadingText default 'Loading...'
30563 * Create a new UploadCropbox
30564 * @param {Object} config The config object
30567 Roo.bootstrap.UploadCropbox = function(config){
30568 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30572 * @event beforeselectfile
30573 * Fire before select file
30574 * @param {Roo.bootstrap.UploadCropbox} this
30576 "beforeselectfile" : true,
30579 * Fire after initEvent
30580 * @param {Roo.bootstrap.UploadCropbox} this
30585 * Fire after initEvent
30586 * @param {Roo.bootstrap.UploadCropbox} this
30587 * @param {String} data
30592 * Fire when preparing the file data
30593 * @param {Roo.bootstrap.UploadCropbox} this
30594 * @param {Object} file
30599 * Fire when get exception
30600 * @param {Roo.bootstrap.UploadCropbox} this
30601 * @param {XMLHttpRequest} xhr
30603 "exception" : true,
30605 * @event beforeloadcanvas
30606 * Fire before load the canvas
30607 * @param {Roo.bootstrap.UploadCropbox} this
30608 * @param {String} src
30610 "beforeloadcanvas" : true,
30613 * Fire when trash image
30614 * @param {Roo.bootstrap.UploadCropbox} this
30619 * Fire when download the image
30620 * @param {Roo.bootstrap.UploadCropbox} this
30624 * @event footerbuttonclick
30625 * Fire when footerbuttonclick
30626 * @param {Roo.bootstrap.UploadCropbox} this
30627 * @param {String} type
30629 "footerbuttonclick" : true,
30633 * @param {Roo.bootstrap.UploadCropbox} this
30638 * Fire when rotate the image
30639 * @param {Roo.bootstrap.UploadCropbox} this
30640 * @param {String} pos
30645 * Fire when inspect the file
30646 * @param {Roo.bootstrap.UploadCropbox} this
30647 * @param {Object} file
30652 * Fire when xhr upload the file
30653 * @param {Roo.bootstrap.UploadCropbox} this
30654 * @param {Object} data
30659 * Fire when arrange the file data
30660 * @param {Roo.bootstrap.UploadCropbox} this
30661 * @param {Object} formData
30666 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30669 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30671 emptyText : 'Click to upload image',
30672 rotateNotify : 'Image is too small to rotate',
30673 errorTimeout : 3000,
30687 cropType : 'image/jpeg',
30689 canvasLoaded : false,
30690 isDocument : false,
30692 paramName : 'imageUpload',
30694 loadingText : 'Loading...',
30697 getAutoCreate : function()
30701 cls : 'roo-upload-cropbox',
30705 cls : 'roo-upload-cropbox-selector',
30710 cls : 'roo-upload-cropbox-body',
30711 style : 'cursor:pointer',
30715 cls : 'roo-upload-cropbox-preview'
30719 cls : 'roo-upload-cropbox-thumb'
30723 cls : 'roo-upload-cropbox-empty-notify',
30724 html : this.emptyText
30728 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30729 html : this.rotateNotify
30735 cls : 'roo-upload-cropbox-footer',
30738 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30748 onRender : function(ct, position)
30750 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30752 if (this.buttons.length) {
30754 Roo.each(this.buttons, function(bb) {
30756 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30758 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30764 this.maskEl = this.el;
30768 initEvents : function()
30770 this.urlAPI = (window.createObjectURL && window) ||
30771 (window.URL && URL.revokeObjectURL && URL) ||
30772 (window.webkitURL && webkitURL);
30774 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30775 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30777 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30778 this.selectorEl.hide();
30780 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30781 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30783 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30784 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30785 this.thumbEl.hide();
30787 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30788 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30790 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30791 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30792 this.errorEl.hide();
30794 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30795 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30796 this.footerEl.hide();
30798 this.setThumbBoxSize();
30804 this.fireEvent('initial', this);
30811 window.addEventListener("resize", function() { _this.resize(); } );
30813 this.bodyEl.on('click', this.beforeSelectFile, this);
30816 this.bodyEl.on('touchstart', this.onTouchStart, this);
30817 this.bodyEl.on('touchmove', this.onTouchMove, this);
30818 this.bodyEl.on('touchend', this.onTouchEnd, this);
30822 this.bodyEl.on('mousedown', this.onMouseDown, this);
30823 this.bodyEl.on('mousemove', this.onMouseMove, this);
30824 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30825 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30826 Roo.get(document).on('mouseup', this.onMouseUp, this);
30829 this.selectorEl.on('change', this.onFileSelected, this);
30835 this.baseScale = 1;
30837 this.baseRotate = 1;
30838 this.dragable = false;
30839 this.pinching = false;
30842 this.cropData = false;
30843 this.notifyEl.dom.innerHTML = this.emptyText;
30845 this.selectorEl.dom.value = '';
30849 resize : function()
30851 if(this.fireEvent('resize', this) != false){
30852 this.setThumbBoxPosition();
30853 this.setCanvasPosition();
30857 onFooterButtonClick : function(e, el, o, type)
30860 case 'rotate-left' :
30861 this.onRotateLeft(e);
30863 case 'rotate-right' :
30864 this.onRotateRight(e);
30867 this.beforeSelectFile(e);
30882 this.fireEvent('footerbuttonclick', this, type);
30885 beforeSelectFile : function(e)
30887 e.preventDefault();
30889 if(this.fireEvent('beforeselectfile', this) != false){
30890 this.selectorEl.dom.click();
30894 onFileSelected : function(e)
30896 e.preventDefault();
30898 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30902 var file = this.selectorEl.dom.files[0];
30904 if(this.fireEvent('inspect', this, file) != false){
30905 this.prepare(file);
30910 trash : function(e)
30912 this.fireEvent('trash', this);
30915 download : function(e)
30917 this.fireEvent('download', this);
30920 loadCanvas : function(src)
30922 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30926 this.imageEl = document.createElement('img');
30930 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30932 this.imageEl.src = src;
30936 onLoadCanvas : function()
30938 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30939 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30941 this.bodyEl.un('click', this.beforeSelectFile, this);
30943 this.notifyEl.hide();
30944 this.thumbEl.show();
30945 this.footerEl.show();
30947 this.baseRotateLevel();
30949 if(this.isDocument){
30950 this.setThumbBoxSize();
30953 this.setThumbBoxPosition();
30955 this.baseScaleLevel();
30961 this.canvasLoaded = true;
30964 this.maskEl.unmask();
30969 setCanvasPosition : function()
30971 if(!this.canvasEl){
30975 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30976 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30978 this.previewEl.setLeft(pw);
30979 this.previewEl.setTop(ph);
30983 onMouseDown : function(e)
30987 this.dragable = true;
30988 this.pinching = false;
30990 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30991 this.dragable = false;
30995 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30996 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31000 onMouseMove : function(e)
31004 if(!this.canvasLoaded){
31008 if (!this.dragable){
31012 var minX = Math.ceil(this.thumbEl.getLeft(true));
31013 var minY = Math.ceil(this.thumbEl.getTop(true));
31015 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31016 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31018 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31019 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31021 x = x - this.mouseX;
31022 y = y - this.mouseY;
31024 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31025 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31027 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31028 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31030 this.previewEl.setLeft(bgX);
31031 this.previewEl.setTop(bgY);
31033 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31034 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31037 onMouseUp : function(e)
31041 this.dragable = false;
31044 onMouseWheel : function(e)
31048 this.startScale = this.scale;
31050 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31052 if(!this.zoomable()){
31053 this.scale = this.startScale;
31062 zoomable : function()
31064 var minScale = this.thumbEl.getWidth() / this.minWidth;
31066 if(this.minWidth < this.minHeight){
31067 minScale = this.thumbEl.getHeight() / this.minHeight;
31070 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31071 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31075 (this.rotate == 0 || this.rotate == 180) &&
31077 width > this.imageEl.OriginWidth ||
31078 height > this.imageEl.OriginHeight ||
31079 (width < this.minWidth && height < this.minHeight)
31087 (this.rotate == 90 || this.rotate == 270) &&
31089 width > this.imageEl.OriginWidth ||
31090 height > this.imageEl.OriginHeight ||
31091 (width < this.minHeight && height < this.minWidth)
31098 !this.isDocument &&
31099 (this.rotate == 0 || this.rotate == 180) &&
31101 width < this.minWidth ||
31102 width > this.imageEl.OriginWidth ||
31103 height < this.minHeight ||
31104 height > this.imageEl.OriginHeight
31111 !this.isDocument &&
31112 (this.rotate == 90 || this.rotate == 270) &&
31114 width < this.minHeight ||
31115 width > this.imageEl.OriginWidth ||
31116 height < this.minWidth ||
31117 height > this.imageEl.OriginHeight
31127 onRotateLeft : function(e)
31129 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31131 var minScale = this.thumbEl.getWidth() / this.minWidth;
31133 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31134 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31136 this.startScale = this.scale;
31138 while (this.getScaleLevel() < minScale){
31140 this.scale = this.scale + 1;
31142 if(!this.zoomable()){
31147 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31148 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31153 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31160 this.scale = this.startScale;
31162 this.onRotateFail();
31167 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31169 if(this.isDocument){
31170 this.setThumbBoxSize();
31171 this.setThumbBoxPosition();
31172 this.setCanvasPosition();
31177 this.fireEvent('rotate', this, 'left');
31181 onRotateRight : function(e)
31183 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31185 var minScale = this.thumbEl.getWidth() / this.minWidth;
31187 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31188 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31190 this.startScale = this.scale;
31192 while (this.getScaleLevel() < minScale){
31194 this.scale = this.scale + 1;
31196 if(!this.zoomable()){
31201 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31202 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31207 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31214 this.scale = this.startScale;
31216 this.onRotateFail();
31221 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31223 if(this.isDocument){
31224 this.setThumbBoxSize();
31225 this.setThumbBoxPosition();
31226 this.setCanvasPosition();
31231 this.fireEvent('rotate', this, 'right');
31234 onRotateFail : function()
31236 this.errorEl.show(true);
31240 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31245 this.previewEl.dom.innerHTML = '';
31247 var canvasEl = document.createElement("canvas");
31249 var contextEl = canvasEl.getContext("2d");
31251 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31252 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31253 var center = this.imageEl.OriginWidth / 2;
31255 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31256 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31257 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31258 center = this.imageEl.OriginHeight / 2;
31261 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31263 contextEl.translate(center, center);
31264 contextEl.rotate(this.rotate * Math.PI / 180);
31266 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31268 this.canvasEl = document.createElement("canvas");
31270 this.contextEl = this.canvasEl.getContext("2d");
31272 switch (this.rotate) {
31275 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31276 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31278 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31283 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31284 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31286 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31287 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);
31291 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31296 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31297 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31299 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31300 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);
31304 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);
31309 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31310 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31312 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31313 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31317 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);
31324 this.previewEl.appendChild(this.canvasEl);
31326 this.setCanvasPosition();
31331 if(!this.canvasLoaded){
31335 var imageCanvas = document.createElement("canvas");
31337 var imageContext = imageCanvas.getContext("2d");
31339 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31340 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31342 var center = imageCanvas.width / 2;
31344 imageContext.translate(center, center);
31346 imageContext.rotate(this.rotate * Math.PI / 180);
31348 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31350 var canvas = document.createElement("canvas");
31352 var context = canvas.getContext("2d");
31354 canvas.width = this.minWidth;
31355 canvas.height = this.minHeight;
31357 switch (this.rotate) {
31360 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31361 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31363 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31364 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31366 var targetWidth = this.minWidth - 2 * x;
31367 var targetHeight = this.minHeight - 2 * y;
31371 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31372 scale = targetWidth / width;
31375 if(x > 0 && y == 0){
31376 scale = targetHeight / height;
31379 if(x > 0 && y > 0){
31380 scale = targetWidth / width;
31382 if(width < height){
31383 scale = targetHeight / height;
31387 context.scale(scale, scale);
31389 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31390 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31392 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31393 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31395 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31400 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31401 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31403 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31404 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31406 var targetWidth = this.minWidth - 2 * x;
31407 var targetHeight = this.minHeight - 2 * y;
31411 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31412 scale = targetWidth / width;
31415 if(x > 0 && y == 0){
31416 scale = targetHeight / height;
31419 if(x > 0 && y > 0){
31420 scale = targetWidth / width;
31422 if(width < height){
31423 scale = targetHeight / height;
31427 context.scale(scale, scale);
31429 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31430 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31432 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31433 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31435 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31437 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31442 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31443 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31445 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31446 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31448 var targetWidth = this.minWidth - 2 * x;
31449 var targetHeight = this.minHeight - 2 * y;
31453 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31454 scale = targetWidth / width;
31457 if(x > 0 && y == 0){
31458 scale = targetHeight / height;
31461 if(x > 0 && y > 0){
31462 scale = targetWidth / width;
31464 if(width < height){
31465 scale = targetHeight / height;
31469 context.scale(scale, scale);
31471 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31472 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31474 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31475 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31477 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31478 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31480 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31485 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31486 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31488 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31489 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31491 var targetWidth = this.minWidth - 2 * x;
31492 var targetHeight = this.minHeight - 2 * y;
31496 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31497 scale = targetWidth / width;
31500 if(x > 0 && y == 0){
31501 scale = targetHeight / height;
31504 if(x > 0 && y > 0){
31505 scale = targetWidth / width;
31507 if(width < height){
31508 scale = targetHeight / height;
31512 context.scale(scale, scale);
31514 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31515 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31517 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31518 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31520 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31522 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31529 this.cropData = canvas.toDataURL(this.cropType);
31531 if(this.fireEvent('crop', this, this.cropData) !== false){
31532 this.process(this.file, this.cropData);
31539 setThumbBoxSize : function()
31543 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31544 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31545 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31547 this.minWidth = width;
31548 this.minHeight = height;
31550 if(this.rotate == 90 || this.rotate == 270){
31551 this.minWidth = height;
31552 this.minHeight = width;
31557 width = Math.ceil(this.minWidth * height / this.minHeight);
31559 if(this.minWidth > this.minHeight){
31561 height = Math.ceil(this.minHeight * width / this.minWidth);
31564 this.thumbEl.setStyle({
31565 width : width + 'px',
31566 height : height + 'px'
31573 setThumbBoxPosition : function()
31575 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31576 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31578 this.thumbEl.setLeft(x);
31579 this.thumbEl.setTop(y);
31583 baseRotateLevel : function()
31585 this.baseRotate = 1;
31588 typeof(this.exif) != 'undefined' &&
31589 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31590 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31592 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31595 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31599 baseScaleLevel : function()
31603 if(this.isDocument){
31605 if(this.baseRotate == 6 || this.baseRotate == 8){
31607 height = this.thumbEl.getHeight();
31608 this.baseScale = height / this.imageEl.OriginWidth;
31610 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31611 width = this.thumbEl.getWidth();
31612 this.baseScale = width / this.imageEl.OriginHeight;
31618 height = this.thumbEl.getHeight();
31619 this.baseScale = height / this.imageEl.OriginHeight;
31621 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31622 width = this.thumbEl.getWidth();
31623 this.baseScale = width / this.imageEl.OriginWidth;
31629 if(this.baseRotate == 6 || this.baseRotate == 8){
31631 width = this.thumbEl.getHeight();
31632 this.baseScale = width / this.imageEl.OriginHeight;
31634 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31635 height = this.thumbEl.getWidth();
31636 this.baseScale = height / this.imageEl.OriginHeight;
31639 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31640 height = this.thumbEl.getWidth();
31641 this.baseScale = height / this.imageEl.OriginHeight;
31643 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31644 width = this.thumbEl.getHeight();
31645 this.baseScale = width / this.imageEl.OriginWidth;
31652 width = this.thumbEl.getWidth();
31653 this.baseScale = width / this.imageEl.OriginWidth;
31655 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31656 height = this.thumbEl.getHeight();
31657 this.baseScale = height / this.imageEl.OriginHeight;
31660 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31662 height = this.thumbEl.getHeight();
31663 this.baseScale = height / this.imageEl.OriginHeight;
31665 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31666 width = this.thumbEl.getWidth();
31667 this.baseScale = width / this.imageEl.OriginWidth;
31675 getScaleLevel : function()
31677 return this.baseScale * Math.pow(1.1, this.scale);
31680 onTouchStart : function(e)
31682 if(!this.canvasLoaded){
31683 this.beforeSelectFile(e);
31687 var touches = e.browserEvent.touches;
31693 if(touches.length == 1){
31694 this.onMouseDown(e);
31698 if(touches.length != 2){
31704 for(var i = 0, finger; finger = touches[i]; i++){
31705 coords.push(finger.pageX, finger.pageY);
31708 var x = Math.pow(coords[0] - coords[2], 2);
31709 var y = Math.pow(coords[1] - coords[3], 2);
31711 this.startDistance = Math.sqrt(x + y);
31713 this.startScale = this.scale;
31715 this.pinching = true;
31716 this.dragable = false;
31720 onTouchMove : function(e)
31722 if(!this.pinching && !this.dragable){
31726 var touches = e.browserEvent.touches;
31733 this.onMouseMove(e);
31739 for(var i = 0, finger; finger = touches[i]; i++){
31740 coords.push(finger.pageX, finger.pageY);
31743 var x = Math.pow(coords[0] - coords[2], 2);
31744 var y = Math.pow(coords[1] - coords[3], 2);
31746 this.endDistance = Math.sqrt(x + y);
31748 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31750 if(!this.zoomable()){
31751 this.scale = this.startScale;
31759 onTouchEnd : function(e)
31761 this.pinching = false;
31762 this.dragable = false;
31766 process : function(file, crop)
31769 this.maskEl.mask(this.loadingText);
31772 this.xhr = new XMLHttpRequest();
31774 file.xhr = this.xhr;
31776 this.xhr.open(this.method, this.url, true);
31779 "Accept": "application/json",
31780 "Cache-Control": "no-cache",
31781 "X-Requested-With": "XMLHttpRequest"
31784 for (var headerName in headers) {
31785 var headerValue = headers[headerName];
31787 this.xhr.setRequestHeader(headerName, headerValue);
31793 this.xhr.onload = function()
31795 _this.xhrOnLoad(_this.xhr);
31798 this.xhr.onerror = function()
31800 _this.xhrOnError(_this.xhr);
31803 var formData = new FormData();
31805 formData.append('returnHTML', 'NO');
31808 formData.append('crop', crop);
31811 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31812 formData.append(this.paramName, file, file.name);
31815 if(typeof(file.filename) != 'undefined'){
31816 formData.append('filename', file.filename);
31819 if(typeof(file.mimetype) != 'undefined'){
31820 formData.append('mimetype', file.mimetype);
31823 if(this.fireEvent('arrange', this, formData) != false){
31824 this.xhr.send(formData);
31828 xhrOnLoad : function(xhr)
31831 this.maskEl.unmask();
31834 if (xhr.readyState !== 4) {
31835 this.fireEvent('exception', this, xhr);
31839 var response = Roo.decode(xhr.responseText);
31841 if(!response.success){
31842 this.fireEvent('exception', this, xhr);
31846 var response = Roo.decode(xhr.responseText);
31848 this.fireEvent('upload', this, response);
31852 xhrOnError : function()
31855 this.maskEl.unmask();
31858 Roo.log('xhr on error');
31860 var response = Roo.decode(xhr.responseText);
31866 prepare : function(file)
31869 this.maskEl.mask(this.loadingText);
31875 if(typeof(file) === 'string'){
31876 this.loadCanvas(file);
31880 if(!file || !this.urlAPI){
31885 this.cropType = file.type;
31889 if(this.fireEvent('prepare', this, this.file) != false){
31891 var reader = new FileReader();
31893 reader.onload = function (e) {
31894 if (e.target.error) {
31895 Roo.log(e.target.error);
31899 var buffer = e.target.result,
31900 dataView = new DataView(buffer),
31902 maxOffset = dataView.byteLength - 4,
31906 if (dataView.getUint16(0) === 0xffd8) {
31907 while (offset < maxOffset) {
31908 markerBytes = dataView.getUint16(offset);
31910 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31911 markerLength = dataView.getUint16(offset + 2) + 2;
31912 if (offset + markerLength > dataView.byteLength) {
31913 Roo.log('Invalid meta data: Invalid segment size.');
31917 if(markerBytes == 0xffe1){
31918 _this.parseExifData(
31925 offset += markerLength;
31935 var url = _this.urlAPI.createObjectURL(_this.file);
31937 _this.loadCanvas(url);
31942 reader.readAsArrayBuffer(this.file);
31948 parseExifData : function(dataView, offset, length)
31950 var tiffOffset = offset + 10,
31954 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31955 // No Exif data, might be XMP data instead
31959 // Check for the ASCII code for "Exif" (0x45786966):
31960 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31961 // No Exif data, might be XMP data instead
31964 if (tiffOffset + 8 > dataView.byteLength) {
31965 Roo.log('Invalid Exif data: Invalid segment size.');
31968 // Check for the two null bytes:
31969 if (dataView.getUint16(offset + 8) !== 0x0000) {
31970 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31973 // Check the byte alignment:
31974 switch (dataView.getUint16(tiffOffset)) {
31976 littleEndian = true;
31979 littleEndian = false;
31982 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31985 // Check for the TIFF tag marker (0x002A):
31986 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31987 Roo.log('Invalid Exif data: Missing TIFF marker.');
31990 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31991 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31993 this.parseExifTags(
31996 tiffOffset + dirOffset,
32001 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32006 if (dirOffset + 6 > dataView.byteLength) {
32007 Roo.log('Invalid Exif data: Invalid directory offset.');
32010 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32011 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32012 if (dirEndOffset + 4 > dataView.byteLength) {
32013 Roo.log('Invalid Exif data: Invalid directory size.');
32016 for (i = 0; i < tagsNumber; i += 1) {
32020 dirOffset + 2 + 12 * i, // tag offset
32024 // Return the offset to the next directory:
32025 return dataView.getUint32(dirEndOffset, littleEndian);
32028 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32030 var tag = dataView.getUint16(offset, littleEndian);
32032 this.exif[tag] = this.getExifValue(
32036 dataView.getUint16(offset + 2, littleEndian), // tag type
32037 dataView.getUint32(offset + 4, littleEndian), // tag length
32042 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32044 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32053 Roo.log('Invalid Exif data: Invalid tag type.');
32057 tagSize = tagType.size * length;
32058 // Determine if the value is contained in the dataOffset bytes,
32059 // or if the value at the dataOffset is a pointer to the actual data:
32060 dataOffset = tagSize > 4 ?
32061 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32062 if (dataOffset + tagSize > dataView.byteLength) {
32063 Roo.log('Invalid Exif data: Invalid data offset.');
32066 if (length === 1) {
32067 return tagType.getValue(dataView, dataOffset, littleEndian);
32070 for (i = 0; i < length; i += 1) {
32071 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32074 if (tagType.ascii) {
32076 // Concatenate the chars:
32077 for (i = 0; i < values.length; i += 1) {
32079 // Ignore the terminating NULL byte(s):
32080 if (c === '\u0000') {
32092 Roo.apply(Roo.bootstrap.UploadCropbox, {
32094 'Orientation': 0x0112
32098 1: 0, //'top-left',
32100 3: 180, //'bottom-right',
32101 // 4: 'bottom-left',
32103 6: 90, //'right-top',
32104 // 7: 'right-bottom',
32105 8: 270 //'left-bottom'
32109 // byte, 8-bit unsigned int:
32111 getValue: function (dataView, dataOffset) {
32112 return dataView.getUint8(dataOffset);
32116 // ascii, 8-bit byte:
32118 getValue: function (dataView, dataOffset) {
32119 return String.fromCharCode(dataView.getUint8(dataOffset));
32124 // short, 16 bit int:
32126 getValue: function (dataView, dataOffset, littleEndian) {
32127 return dataView.getUint16(dataOffset, littleEndian);
32131 // long, 32 bit int:
32133 getValue: function (dataView, dataOffset, littleEndian) {
32134 return dataView.getUint32(dataOffset, littleEndian);
32138 // rational = two long values, first is numerator, second is denominator:
32140 getValue: function (dataView, dataOffset, littleEndian) {
32141 return dataView.getUint32(dataOffset, littleEndian) /
32142 dataView.getUint32(dataOffset + 4, littleEndian);
32146 // slong, 32 bit signed int:
32148 getValue: function (dataView, dataOffset, littleEndian) {
32149 return dataView.getInt32(dataOffset, littleEndian);
32153 // srational, two slongs, first is numerator, second is denominator:
32155 getValue: function (dataView, dataOffset, littleEndian) {
32156 return dataView.getInt32(dataOffset, littleEndian) /
32157 dataView.getInt32(dataOffset + 4, littleEndian);
32167 cls : 'btn-group roo-upload-cropbox-rotate-left',
32168 action : 'rotate-left',
32172 cls : 'btn btn-default',
32173 html : '<i class="fa fa-undo"></i>'
32179 cls : 'btn-group roo-upload-cropbox-picture',
32180 action : 'picture',
32184 cls : 'btn btn-default',
32185 html : '<i class="fa fa-picture-o"></i>'
32191 cls : 'btn-group roo-upload-cropbox-rotate-right',
32192 action : 'rotate-right',
32196 cls : 'btn btn-default',
32197 html : '<i class="fa fa-repeat"></i>'
32205 cls : 'btn-group roo-upload-cropbox-rotate-left',
32206 action : 'rotate-left',
32210 cls : 'btn btn-default',
32211 html : '<i class="fa fa-undo"></i>'
32217 cls : 'btn-group roo-upload-cropbox-download',
32218 action : 'download',
32222 cls : 'btn btn-default',
32223 html : '<i class="fa fa-download"></i>'
32229 cls : 'btn-group roo-upload-cropbox-crop',
32234 cls : 'btn btn-default',
32235 html : '<i class="fa fa-crop"></i>'
32241 cls : 'btn-group roo-upload-cropbox-trash',
32246 cls : 'btn btn-default',
32247 html : '<i class="fa fa-trash"></i>'
32253 cls : 'btn-group roo-upload-cropbox-rotate-right',
32254 action : 'rotate-right',
32258 cls : 'btn btn-default',
32259 html : '<i class="fa fa-repeat"></i>'
32267 cls : 'btn-group roo-upload-cropbox-rotate-left',
32268 action : 'rotate-left',
32272 cls : 'btn btn-default',
32273 html : '<i class="fa fa-undo"></i>'
32279 cls : 'btn-group roo-upload-cropbox-rotate-right',
32280 action : 'rotate-right',
32284 cls : 'btn btn-default',
32285 html : '<i class="fa fa-repeat"></i>'
32298 * @class Roo.bootstrap.DocumentManager
32299 * @extends Roo.bootstrap.Component
32300 * Bootstrap DocumentManager class
32301 * @cfg {String} paramName default 'imageUpload'
32302 * @cfg {String} toolTipName default 'filename'
32303 * @cfg {String} method default POST
32304 * @cfg {String} url action url
32305 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32306 * @cfg {Boolean} multiple multiple upload default true
32307 * @cfg {Number} thumbSize default 300
32308 * @cfg {String} fieldLabel
32309 * @cfg {Number} labelWidth default 4
32310 * @cfg {String} labelAlign (left|top) default left
32311 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32312 * @cfg {Number} labellg set the width of label (1-12)
32313 * @cfg {Number} labelmd set the width of label (1-12)
32314 * @cfg {Number} labelsm set the width of label (1-12)
32315 * @cfg {Number} labelxs set the width of label (1-12)
32318 * Create a new DocumentManager
32319 * @param {Object} config The config object
32322 Roo.bootstrap.DocumentManager = function(config){
32323 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32326 this.delegates = [];
32331 * Fire when initial the DocumentManager
32332 * @param {Roo.bootstrap.DocumentManager} this
32337 * inspect selected file
32338 * @param {Roo.bootstrap.DocumentManager} this
32339 * @param {File} file
32344 * Fire when xhr load exception
32345 * @param {Roo.bootstrap.DocumentManager} this
32346 * @param {XMLHttpRequest} xhr
32348 "exception" : true,
32350 * @event afterupload
32351 * Fire when xhr load exception
32352 * @param {Roo.bootstrap.DocumentManager} this
32353 * @param {XMLHttpRequest} xhr
32355 "afterupload" : true,
32358 * prepare the form data
32359 * @param {Roo.bootstrap.DocumentManager} this
32360 * @param {Object} formData
32365 * Fire when remove the file
32366 * @param {Roo.bootstrap.DocumentManager} this
32367 * @param {Object} file
32372 * Fire after refresh the file
32373 * @param {Roo.bootstrap.DocumentManager} this
32378 * Fire after click the image
32379 * @param {Roo.bootstrap.DocumentManager} this
32380 * @param {Object} file
32385 * Fire when upload a image and editable set to true
32386 * @param {Roo.bootstrap.DocumentManager} this
32387 * @param {Object} file
32391 * @event beforeselectfile
32392 * Fire before select file
32393 * @param {Roo.bootstrap.DocumentManager} this
32395 "beforeselectfile" : true,
32398 * Fire before process file
32399 * @param {Roo.bootstrap.DocumentManager} this
32400 * @param {Object} file
32404 * @event previewrendered
32405 * Fire when preview rendered
32406 * @param {Roo.bootstrap.DocumentManager} this
32407 * @param {Object} file
32409 "previewrendered" : true,
32412 "previewResize" : true
32417 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32426 paramName : 'imageUpload',
32427 toolTipName : 'filename',
32430 labelAlign : 'left',
32440 getAutoCreate : function()
32442 var managerWidget = {
32444 cls : 'roo-document-manager',
32448 cls : 'roo-document-manager-selector',
32453 cls : 'roo-document-manager-uploader',
32457 cls : 'roo-document-manager-upload-btn',
32458 html : '<i class="fa fa-plus"></i>'
32469 cls : 'column col-md-12',
32474 if(this.fieldLabel.length){
32479 cls : 'column col-md-12',
32480 html : this.fieldLabel
32484 cls : 'column col-md-12',
32489 if(this.labelAlign == 'left'){
32494 html : this.fieldLabel
32503 if(this.labelWidth > 12){
32504 content[0].style = "width: " + this.labelWidth + 'px';
32507 if(this.labelWidth < 13 && this.labelmd == 0){
32508 this.labelmd = this.labelWidth;
32511 if(this.labellg > 0){
32512 content[0].cls += ' col-lg-' + this.labellg;
32513 content[1].cls += ' col-lg-' + (12 - this.labellg);
32516 if(this.labelmd > 0){
32517 content[0].cls += ' col-md-' + this.labelmd;
32518 content[1].cls += ' col-md-' + (12 - this.labelmd);
32521 if(this.labelsm > 0){
32522 content[0].cls += ' col-sm-' + this.labelsm;
32523 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32526 if(this.labelxs > 0){
32527 content[0].cls += ' col-xs-' + this.labelxs;
32528 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32536 cls : 'row clearfix',
32544 initEvents : function()
32546 this.managerEl = this.el.select('.roo-document-manager', true).first();
32547 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32549 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32550 this.selectorEl.hide();
32553 this.selectorEl.attr('multiple', 'multiple');
32556 this.selectorEl.on('change', this.onFileSelected, this);
32558 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32559 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32561 this.uploader.on('click', this.onUploaderClick, this);
32563 this.renderProgressDialog();
32567 window.addEventListener("resize", function() { _this.refresh(); } );
32569 this.fireEvent('initial', this);
32572 renderProgressDialog : function()
32576 this.progressDialog = new Roo.bootstrap.Modal({
32577 cls : 'roo-document-manager-progress-dialog',
32578 allow_close : false,
32589 btnclick : function() {
32590 _this.uploadCancel();
32596 this.progressDialog.render(Roo.get(document.body));
32598 this.progress = new Roo.bootstrap.Progress({
32599 cls : 'roo-document-manager-progress',
32604 this.progress.render(this.progressDialog.getChildContainer());
32606 this.progressBar = new Roo.bootstrap.ProgressBar({
32607 cls : 'roo-document-manager-progress-bar',
32610 aria_valuemax : 12,
32614 this.progressBar.render(this.progress.getChildContainer());
32617 onUploaderClick : function(e)
32619 e.preventDefault();
32621 if(this.fireEvent('beforeselectfile', this) != false){
32622 this.selectorEl.dom.click();
32627 onFileSelected : function(e)
32629 e.preventDefault();
32631 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32635 Roo.each(this.selectorEl.dom.files, function(file){
32636 if(this.fireEvent('inspect', this, file) != false){
32637 this.files.push(file);
32647 this.selectorEl.dom.value = '';
32649 if(!this.files || !this.files.length){
32653 if(this.boxes > 0 && this.files.length > this.boxes){
32654 this.files = this.files.slice(0, this.boxes);
32657 this.uploader.show();
32659 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32660 this.uploader.hide();
32669 Roo.each(this.files, function(file){
32671 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32672 var f = this.renderPreview(file);
32677 if(file.type.indexOf('image') != -1){
32678 this.delegates.push(
32680 _this.process(file);
32681 }).createDelegate(this)
32689 _this.process(file);
32690 }).createDelegate(this)
32695 this.files = files;
32697 this.delegates = this.delegates.concat(docs);
32699 if(!this.delegates.length){
32704 this.progressBar.aria_valuemax = this.delegates.length;
32711 arrange : function()
32713 if(!this.delegates.length){
32714 this.progressDialog.hide();
32719 var delegate = this.delegates.shift();
32721 this.progressDialog.show();
32723 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32725 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32730 refresh : function()
32732 this.uploader.show();
32734 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32735 this.uploader.hide();
32738 Roo.isTouch ? this.closable(false) : this.closable(true);
32740 this.fireEvent('refresh', this);
32743 onRemove : function(e, el, o)
32745 e.preventDefault();
32747 this.fireEvent('remove', this, o);
32751 remove : function(o)
32755 Roo.each(this.files, function(file){
32756 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32765 this.files = files;
32772 Roo.each(this.files, function(file){
32777 file.target.remove();
32786 onClick : function(e, el, o)
32788 e.preventDefault();
32790 this.fireEvent('click', this, o);
32794 closable : function(closable)
32796 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32798 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32810 xhrOnLoad : function(xhr)
32812 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32816 if (xhr.readyState !== 4) {
32818 this.fireEvent('exception', this, xhr);
32822 var response = Roo.decode(xhr.responseText);
32824 if(!response.success){
32826 this.fireEvent('exception', this, xhr);
32830 var file = this.renderPreview(response.data);
32832 this.files.push(file);
32836 this.fireEvent('afterupload', this, xhr);
32840 xhrOnError : function(xhr)
32842 Roo.log('xhr on error');
32844 var response = Roo.decode(xhr.responseText);
32851 process : function(file)
32853 if(this.fireEvent('process', this, file) !== false){
32854 if(this.editable && file.type.indexOf('image') != -1){
32855 this.fireEvent('edit', this, file);
32859 this.uploadStart(file, false);
32866 uploadStart : function(file, crop)
32868 this.xhr = new XMLHttpRequest();
32870 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32875 file.xhr = this.xhr;
32877 this.managerEl.createChild({
32879 cls : 'roo-document-manager-loading',
32883 tooltip : file.name,
32884 cls : 'roo-document-manager-thumb',
32885 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32891 this.xhr.open(this.method, this.url, true);
32894 "Accept": "application/json",
32895 "Cache-Control": "no-cache",
32896 "X-Requested-With": "XMLHttpRequest"
32899 for (var headerName in headers) {
32900 var headerValue = headers[headerName];
32902 this.xhr.setRequestHeader(headerName, headerValue);
32908 this.xhr.onload = function()
32910 _this.xhrOnLoad(_this.xhr);
32913 this.xhr.onerror = function()
32915 _this.xhrOnError(_this.xhr);
32918 var formData = new FormData();
32920 formData.append('returnHTML', 'NO');
32923 formData.append('crop', crop);
32926 formData.append(this.paramName, file, file.name);
32933 if(this.fireEvent('prepare', this, formData, options) != false){
32935 if(options.manually){
32939 this.xhr.send(formData);
32943 this.uploadCancel();
32946 uploadCancel : function()
32952 this.delegates = [];
32954 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32961 renderPreview : function(file)
32963 if(typeof(file.target) != 'undefined' && file.target){
32967 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32969 var previewEl = this.managerEl.createChild({
32971 cls : 'roo-document-manager-preview',
32975 tooltip : file[this.toolTipName],
32976 cls : 'roo-document-manager-thumb',
32977 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32982 html : '<i class="fa fa-times-circle"></i>'
32987 var close = previewEl.select('button.close', true).first();
32989 close.on('click', this.onRemove, this, file);
32991 file.target = previewEl;
32993 var image = previewEl.select('img', true).first();
32997 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32999 image.on('click', this.onClick, this, file);
33001 this.fireEvent('previewrendered', this, file);
33007 onPreviewLoad : function(file, image)
33009 if(typeof(file.target) == 'undefined' || !file.target){
33013 var width = image.dom.naturalWidth || image.dom.width;
33014 var height = image.dom.naturalHeight || image.dom.height;
33016 if(!this.previewResize) {
33020 if(width > height){
33021 file.target.addClass('wide');
33025 file.target.addClass('tall');
33030 uploadFromSource : function(file, crop)
33032 this.xhr = new XMLHttpRequest();
33034 this.managerEl.createChild({
33036 cls : 'roo-document-manager-loading',
33040 tooltip : file.name,
33041 cls : 'roo-document-manager-thumb',
33042 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33048 this.xhr.open(this.method, this.url, true);
33051 "Accept": "application/json",
33052 "Cache-Control": "no-cache",
33053 "X-Requested-With": "XMLHttpRequest"
33056 for (var headerName in headers) {
33057 var headerValue = headers[headerName];
33059 this.xhr.setRequestHeader(headerName, headerValue);
33065 this.xhr.onload = function()
33067 _this.xhrOnLoad(_this.xhr);
33070 this.xhr.onerror = function()
33072 _this.xhrOnError(_this.xhr);
33075 var formData = new FormData();
33077 formData.append('returnHTML', 'NO');
33079 formData.append('crop', crop);
33081 if(typeof(file.filename) != 'undefined'){
33082 formData.append('filename', file.filename);
33085 if(typeof(file.mimetype) != 'undefined'){
33086 formData.append('mimetype', file.mimetype);
33091 if(this.fireEvent('prepare', this, formData) != false){
33092 this.xhr.send(formData);
33102 * @class Roo.bootstrap.DocumentViewer
33103 * @extends Roo.bootstrap.Component
33104 * Bootstrap DocumentViewer class
33105 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33106 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33109 * Create a new DocumentViewer
33110 * @param {Object} config The config object
33113 Roo.bootstrap.DocumentViewer = function(config){
33114 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33119 * Fire after initEvent
33120 * @param {Roo.bootstrap.DocumentViewer} this
33126 * @param {Roo.bootstrap.DocumentViewer} this
33131 * Fire after download button
33132 * @param {Roo.bootstrap.DocumentViewer} this
33137 * Fire after trash button
33138 * @param {Roo.bootstrap.DocumentViewer} this
33145 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33147 showDownload : true,
33151 getAutoCreate : function()
33155 cls : 'roo-document-viewer',
33159 cls : 'roo-document-viewer-body',
33163 cls : 'roo-document-viewer-thumb',
33167 cls : 'roo-document-viewer-image'
33175 cls : 'roo-document-viewer-footer',
33178 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33182 cls : 'btn-group roo-document-viewer-download',
33186 cls : 'btn btn-default',
33187 html : '<i class="fa fa-download"></i>'
33193 cls : 'btn-group roo-document-viewer-trash',
33197 cls : 'btn btn-default',
33198 html : '<i class="fa fa-trash"></i>'
33211 initEvents : function()
33213 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33214 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33216 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33217 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33219 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33220 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33222 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33223 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33225 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33226 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33228 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33229 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33231 this.bodyEl.on('click', this.onClick, this);
33232 this.downloadBtn.on('click', this.onDownload, this);
33233 this.trashBtn.on('click', this.onTrash, this);
33235 this.downloadBtn.hide();
33236 this.trashBtn.hide();
33238 if(this.showDownload){
33239 this.downloadBtn.show();
33242 if(this.showTrash){
33243 this.trashBtn.show();
33246 if(!this.showDownload && !this.showTrash) {
33247 this.footerEl.hide();
33252 initial : function()
33254 this.fireEvent('initial', this);
33258 onClick : function(e)
33260 e.preventDefault();
33262 this.fireEvent('click', this);
33265 onDownload : function(e)
33267 e.preventDefault();
33269 this.fireEvent('download', this);
33272 onTrash : function(e)
33274 e.preventDefault();
33276 this.fireEvent('trash', this);
33288 * @class Roo.bootstrap.NavProgressBar
33289 * @extends Roo.bootstrap.Component
33290 * Bootstrap NavProgressBar class
33293 * Create a new nav progress bar
33294 * @param {Object} config The config object
33297 Roo.bootstrap.NavProgressBar = function(config){
33298 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33300 this.bullets = this.bullets || [];
33302 // Roo.bootstrap.NavProgressBar.register(this);
33306 * Fires when the active item changes
33307 * @param {Roo.bootstrap.NavProgressBar} this
33308 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33309 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33316 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33321 getAutoCreate : function()
33323 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33327 cls : 'roo-navigation-bar-group',
33331 cls : 'roo-navigation-top-bar'
33335 cls : 'roo-navigation-bullets-bar',
33339 cls : 'roo-navigation-bar'
33346 cls : 'roo-navigation-bottom-bar'
33356 initEvents: function()
33361 onRender : function(ct, position)
33363 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33365 if(this.bullets.length){
33366 Roo.each(this.bullets, function(b){
33375 addItem : function(cfg)
33377 var item = new Roo.bootstrap.NavProgressItem(cfg);
33379 item.parentId = this.id;
33380 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33383 var top = new Roo.bootstrap.Element({
33385 cls : 'roo-navigation-bar-text'
33388 var bottom = new Roo.bootstrap.Element({
33390 cls : 'roo-navigation-bar-text'
33393 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33394 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33396 var topText = new Roo.bootstrap.Element({
33398 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33401 var bottomText = new Roo.bootstrap.Element({
33403 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33406 topText.onRender(top.el, null);
33407 bottomText.onRender(bottom.el, null);
33410 item.bottomEl = bottom;
33413 this.barItems.push(item);
33418 getActive : function()
33420 var active = false;
33422 Roo.each(this.barItems, function(v){
33424 if (!v.isActive()) {
33436 setActiveItem : function(item)
33440 Roo.each(this.barItems, function(v){
33441 if (v.rid == item.rid) {
33445 if (v.isActive()) {
33446 v.setActive(false);
33451 item.setActive(true);
33453 this.fireEvent('changed', this, item, prev);
33456 getBarItem: function(rid)
33460 Roo.each(this.barItems, function(e) {
33461 if (e.rid != rid) {
33472 indexOfItem : function(item)
33476 Roo.each(this.barItems, function(v, i){
33478 if (v.rid != item.rid) {
33489 setActiveNext : function()
33491 var i = this.indexOfItem(this.getActive());
33493 if (i > this.barItems.length) {
33497 this.setActiveItem(this.barItems[i+1]);
33500 setActivePrev : function()
33502 var i = this.indexOfItem(this.getActive());
33508 this.setActiveItem(this.barItems[i-1]);
33511 format : function()
33513 if(!this.barItems.length){
33517 var width = 100 / this.barItems.length;
33519 Roo.each(this.barItems, function(i){
33520 i.el.setStyle('width', width + '%');
33521 i.topEl.el.setStyle('width', width + '%');
33522 i.bottomEl.el.setStyle('width', width + '%');
33531 * Nav Progress Item
33536 * @class Roo.bootstrap.NavProgressItem
33537 * @extends Roo.bootstrap.Component
33538 * Bootstrap NavProgressItem class
33539 * @cfg {String} rid the reference id
33540 * @cfg {Boolean} active (true|false) Is item active default false
33541 * @cfg {Boolean} disabled (true|false) Is item active default false
33542 * @cfg {String} html
33543 * @cfg {String} position (top|bottom) text position default bottom
33544 * @cfg {String} icon show icon instead of number
33547 * Create a new NavProgressItem
33548 * @param {Object} config The config object
33550 Roo.bootstrap.NavProgressItem = function(config){
33551 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33556 * The raw click event for the entire grid.
33557 * @param {Roo.bootstrap.NavProgressItem} this
33558 * @param {Roo.EventObject} e
33565 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33571 position : 'bottom',
33574 getAutoCreate : function()
33576 var iconCls = 'roo-navigation-bar-item-icon';
33578 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33582 cls: 'roo-navigation-bar-item',
33592 cfg.cls += ' active';
33595 cfg.cls += ' disabled';
33601 disable : function()
33603 this.setDisabled(true);
33606 enable : function()
33608 this.setDisabled(false);
33611 initEvents: function()
33613 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33615 this.iconEl.on('click', this.onClick, this);
33618 onClick : function(e)
33620 e.preventDefault();
33626 if(this.fireEvent('click', this, e) === false){
33630 this.parent().setActiveItem(this);
33633 isActive: function ()
33635 return this.active;
33638 setActive : function(state)
33640 if(this.active == state){
33644 this.active = state;
33647 this.el.addClass('active');
33651 this.el.removeClass('active');
33656 setDisabled : function(state)
33658 if(this.disabled == state){
33662 this.disabled = state;
33665 this.el.addClass('disabled');
33669 this.el.removeClass('disabled');
33672 tooltipEl : function()
33674 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33687 * @class Roo.bootstrap.FieldLabel
33688 * @extends Roo.bootstrap.Component
33689 * Bootstrap FieldLabel class
33690 * @cfg {String} html contents of the element
33691 * @cfg {String} tag tag of the element default label
33692 * @cfg {String} cls class of the element
33693 * @cfg {String} target label target
33694 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33695 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33696 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33697 * @cfg {String} iconTooltip default "This field is required"
33698 * @cfg {String} indicatorpos (left|right) default left
33701 * Create a new FieldLabel
33702 * @param {Object} config The config object
33705 Roo.bootstrap.FieldLabel = function(config){
33706 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33711 * Fires after the field has been marked as invalid.
33712 * @param {Roo.form.FieldLabel} this
33713 * @param {String} msg The validation message
33718 * Fires after the field has been validated with no errors.
33719 * @param {Roo.form.FieldLabel} this
33725 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33732 invalidClass : 'has-warning',
33733 validClass : 'has-success',
33734 iconTooltip : 'This field is required',
33735 indicatorpos : 'left',
33737 getAutoCreate : function(){
33740 if (!this.allowBlank) {
33746 cls : 'roo-bootstrap-field-label ' + this.cls,
33751 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33752 tooltip : this.iconTooltip
33761 if(this.indicatorpos == 'right'){
33764 cls : 'roo-bootstrap-field-label ' + this.cls,
33773 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33774 tooltip : this.iconTooltip
33783 initEvents: function()
33785 Roo.bootstrap.Element.superclass.initEvents.call(this);
33787 this.indicator = this.indicatorEl();
33789 if(this.indicator){
33790 this.indicator.removeClass('visible');
33791 this.indicator.addClass('invisible');
33794 Roo.bootstrap.FieldLabel.register(this);
33797 indicatorEl : function()
33799 var indicator = this.el.select('i.roo-required-indicator',true).first();
33810 * Mark this field as valid
33812 markValid : function()
33814 if(this.indicator){
33815 this.indicator.removeClass('visible');
33816 this.indicator.addClass('invisible');
33818 if (Roo.bootstrap.version == 3) {
33819 this.el.removeClass(this.invalidClass);
33820 this.el.addClass(this.validClass);
33822 this.el.removeClass('is-invalid');
33823 this.el.addClass('is-valid');
33827 this.fireEvent('valid', this);
33831 * Mark this field as invalid
33832 * @param {String} msg The validation message
33834 markInvalid : function(msg)
33836 if(this.indicator){
33837 this.indicator.removeClass('invisible');
33838 this.indicator.addClass('visible');
33840 if (Roo.bootstrap.version == 3) {
33841 this.el.removeClass(this.validClass);
33842 this.el.addClass(this.invalidClass);
33844 this.el.removeClass('is-valid');
33845 this.el.addClass('is-invalid');
33849 this.fireEvent('invalid', this, msg);
33855 Roo.apply(Roo.bootstrap.FieldLabel, {
33860 * register a FieldLabel Group
33861 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33863 register : function(label)
33865 if(this.groups.hasOwnProperty(label.target)){
33869 this.groups[label.target] = label;
33873 * fetch a FieldLabel Group based on the target
33874 * @param {string} target
33875 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33877 get: function(target) {
33878 if (typeof(this.groups[target]) == 'undefined') {
33882 return this.groups[target] ;
33891 * page DateSplitField.
33897 * @class Roo.bootstrap.DateSplitField
33898 * @extends Roo.bootstrap.Component
33899 * Bootstrap DateSplitField class
33900 * @cfg {string} fieldLabel - the label associated
33901 * @cfg {Number} labelWidth set the width of label (0-12)
33902 * @cfg {String} labelAlign (top|left)
33903 * @cfg {Boolean} dayAllowBlank (true|false) default false
33904 * @cfg {Boolean} monthAllowBlank (true|false) default false
33905 * @cfg {Boolean} yearAllowBlank (true|false) default false
33906 * @cfg {string} dayPlaceholder
33907 * @cfg {string} monthPlaceholder
33908 * @cfg {string} yearPlaceholder
33909 * @cfg {string} dayFormat default 'd'
33910 * @cfg {string} monthFormat default 'm'
33911 * @cfg {string} yearFormat default 'Y'
33912 * @cfg {Number} labellg set the width of label (1-12)
33913 * @cfg {Number} labelmd set the width of label (1-12)
33914 * @cfg {Number} labelsm set the width of label (1-12)
33915 * @cfg {Number} labelxs set the width of label (1-12)
33919 * Create a new DateSplitField
33920 * @param {Object} config The config object
33923 Roo.bootstrap.DateSplitField = function(config){
33924 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33930 * getting the data of years
33931 * @param {Roo.bootstrap.DateSplitField} this
33932 * @param {Object} years
33937 * getting the data of days
33938 * @param {Roo.bootstrap.DateSplitField} this
33939 * @param {Object} days
33944 * Fires after the field has been marked as invalid.
33945 * @param {Roo.form.Field} this
33946 * @param {String} msg The validation message
33951 * Fires after the field has been validated with no errors.
33952 * @param {Roo.form.Field} this
33958 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33961 labelAlign : 'top',
33963 dayAllowBlank : false,
33964 monthAllowBlank : false,
33965 yearAllowBlank : false,
33966 dayPlaceholder : '',
33967 monthPlaceholder : '',
33968 yearPlaceholder : '',
33972 isFormField : true,
33978 getAutoCreate : function()
33982 cls : 'row roo-date-split-field-group',
33987 cls : 'form-hidden-field roo-date-split-field-group-value',
33993 var labelCls = 'col-md-12';
33994 var contentCls = 'col-md-4';
33996 if(this.fieldLabel){
34000 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34004 html : this.fieldLabel
34009 if(this.labelAlign == 'left'){
34011 if(this.labelWidth > 12){
34012 label.style = "width: " + this.labelWidth + 'px';
34015 if(this.labelWidth < 13 && this.labelmd == 0){
34016 this.labelmd = this.labelWidth;
34019 if(this.labellg > 0){
34020 labelCls = ' col-lg-' + this.labellg;
34021 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34024 if(this.labelmd > 0){
34025 labelCls = ' col-md-' + this.labelmd;
34026 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34029 if(this.labelsm > 0){
34030 labelCls = ' col-sm-' + this.labelsm;
34031 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34034 if(this.labelxs > 0){
34035 labelCls = ' col-xs-' + this.labelxs;
34036 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34040 label.cls += ' ' + labelCls;
34042 cfg.cn.push(label);
34045 Roo.each(['day', 'month', 'year'], function(t){
34048 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34055 inputEl: function ()
34057 return this.el.select('.roo-date-split-field-group-value', true).first();
34060 onRender : function(ct, position)
34064 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34066 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34068 this.dayField = new Roo.bootstrap.ComboBox({
34069 allowBlank : this.dayAllowBlank,
34070 alwaysQuery : true,
34071 displayField : 'value',
34074 forceSelection : true,
34076 placeholder : this.dayPlaceholder,
34077 selectOnFocus : true,
34078 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34079 triggerAction : 'all',
34081 valueField : 'value',
34082 store : new Roo.data.SimpleStore({
34083 data : (function() {
34085 _this.fireEvent('days', _this, days);
34088 fields : [ 'value' ]
34091 select : function (_self, record, index)
34093 _this.setValue(_this.getValue());
34098 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34100 this.monthField = new Roo.bootstrap.MonthField({
34101 after : '<i class=\"fa fa-calendar\"></i>',
34102 allowBlank : this.monthAllowBlank,
34103 placeholder : this.monthPlaceholder,
34106 render : function (_self)
34108 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34109 e.preventDefault();
34113 select : function (_self, oldvalue, newvalue)
34115 _this.setValue(_this.getValue());
34120 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34122 this.yearField = new Roo.bootstrap.ComboBox({
34123 allowBlank : this.yearAllowBlank,
34124 alwaysQuery : true,
34125 displayField : 'value',
34128 forceSelection : true,
34130 placeholder : this.yearPlaceholder,
34131 selectOnFocus : true,
34132 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34133 triggerAction : 'all',
34135 valueField : 'value',
34136 store : new Roo.data.SimpleStore({
34137 data : (function() {
34139 _this.fireEvent('years', _this, years);
34142 fields : [ 'value' ]
34145 select : function (_self, record, index)
34147 _this.setValue(_this.getValue());
34152 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34155 setValue : function(v, format)
34157 this.inputEl.dom.value = v;
34159 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34161 var d = Date.parseDate(v, f);
34168 this.setDay(d.format(this.dayFormat));
34169 this.setMonth(d.format(this.monthFormat));
34170 this.setYear(d.format(this.yearFormat));
34177 setDay : function(v)
34179 this.dayField.setValue(v);
34180 this.inputEl.dom.value = this.getValue();
34185 setMonth : function(v)
34187 this.monthField.setValue(v, true);
34188 this.inputEl.dom.value = this.getValue();
34193 setYear : function(v)
34195 this.yearField.setValue(v);
34196 this.inputEl.dom.value = this.getValue();
34201 getDay : function()
34203 return this.dayField.getValue();
34206 getMonth : function()
34208 return this.monthField.getValue();
34211 getYear : function()
34213 return this.yearField.getValue();
34216 getValue : function()
34218 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34220 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34230 this.inputEl.dom.value = '';
34235 validate : function()
34237 var d = this.dayField.validate();
34238 var m = this.monthField.validate();
34239 var y = this.yearField.validate();
34244 (!this.dayAllowBlank && !d) ||
34245 (!this.monthAllowBlank && !m) ||
34246 (!this.yearAllowBlank && !y)
34251 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34260 this.markInvalid();
34265 markValid : function()
34268 var label = this.el.select('label', true).first();
34269 var icon = this.el.select('i.fa-star', true).first();
34275 this.fireEvent('valid', this);
34279 * Mark this field as invalid
34280 * @param {String} msg The validation message
34282 markInvalid : function(msg)
34285 var label = this.el.select('label', true).first();
34286 var icon = this.el.select('i.fa-star', true).first();
34288 if(label && !icon){
34289 this.el.select('.roo-date-split-field-label', true).createChild({
34291 cls : 'text-danger fa fa-lg fa-star',
34292 tooltip : 'This field is required',
34293 style : 'margin-right:5px;'
34297 this.fireEvent('invalid', this, msg);
34300 clearInvalid : function()
34302 var label = this.el.select('label', true).first();
34303 var icon = this.el.select('i.fa-star', true).first();
34309 this.fireEvent('valid', this);
34312 getName: function()
34322 * http://masonry.desandro.com
34324 * The idea is to render all the bricks based on vertical width...
34326 * The original code extends 'outlayer' - we might need to use that....
34332 * @class Roo.bootstrap.LayoutMasonry
34333 * @extends Roo.bootstrap.Component
34334 * Bootstrap Layout Masonry class
34337 * Create a new Element
34338 * @param {Object} config The config object
34341 Roo.bootstrap.LayoutMasonry = function(config){
34343 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34347 Roo.bootstrap.LayoutMasonry.register(this);
34353 * Fire after layout the items
34354 * @param {Roo.bootstrap.LayoutMasonry} this
34355 * @param {Roo.EventObject} e
34362 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34365 * @cfg {Boolean} isLayoutInstant = no animation?
34367 isLayoutInstant : false, // needed?
34370 * @cfg {Number} boxWidth width of the columns
34375 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34380 * @cfg {Number} padWidth padding below box..
34385 * @cfg {Number} gutter gutter width..
34390 * @cfg {Number} maxCols maximum number of columns
34396 * @cfg {Boolean} isAutoInitial defalut true
34398 isAutoInitial : true,
34403 * @cfg {Boolean} isHorizontal defalut false
34405 isHorizontal : false,
34407 currentSize : null,
34413 bricks: null, //CompositeElement
34417 _isLayoutInited : false,
34419 // isAlternative : false, // only use for vertical layout...
34422 * @cfg {Number} alternativePadWidth padding below box..
34424 alternativePadWidth : 50,
34426 selectedBrick : [],
34428 getAutoCreate : function(){
34430 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34434 cls: 'blog-masonary-wrapper ' + this.cls,
34436 cls : 'mas-boxes masonary'
34443 getChildContainer: function( )
34445 if (this.boxesEl) {
34446 return this.boxesEl;
34449 this.boxesEl = this.el.select('.mas-boxes').first();
34451 return this.boxesEl;
34455 initEvents : function()
34459 if(this.isAutoInitial){
34460 Roo.log('hook children rendered');
34461 this.on('childrenrendered', function() {
34462 Roo.log('children rendered');
34468 initial : function()
34470 this.selectedBrick = [];
34472 this.currentSize = this.el.getBox(true);
34474 Roo.EventManager.onWindowResize(this.resize, this);
34476 if(!this.isAutoInitial){
34484 //this.layout.defer(500,this);
34488 resize : function()
34490 var cs = this.el.getBox(true);
34493 this.currentSize.width == cs.width &&
34494 this.currentSize.x == cs.x &&
34495 this.currentSize.height == cs.height &&
34496 this.currentSize.y == cs.y
34498 Roo.log("no change in with or X or Y");
34502 this.currentSize = cs;
34508 layout : function()
34510 this._resetLayout();
34512 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34514 this.layoutItems( isInstant );
34516 this._isLayoutInited = true;
34518 this.fireEvent('layout', this);
34522 _resetLayout : function()
34524 if(this.isHorizontal){
34525 this.horizontalMeasureColumns();
34529 this.verticalMeasureColumns();
34533 verticalMeasureColumns : function()
34535 this.getContainerWidth();
34537 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34538 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34542 var boxWidth = this.boxWidth + this.padWidth;
34544 if(this.containerWidth < this.boxWidth){
34545 boxWidth = this.containerWidth
34548 var containerWidth = this.containerWidth;
34550 var cols = Math.floor(containerWidth / boxWidth);
34552 this.cols = Math.max( cols, 1 );
34554 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34556 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34558 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34560 this.colWidth = boxWidth + avail - this.padWidth;
34562 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34563 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34566 horizontalMeasureColumns : function()
34568 this.getContainerWidth();
34570 var boxWidth = this.boxWidth;
34572 if(this.containerWidth < boxWidth){
34573 boxWidth = this.containerWidth;
34576 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34578 this.el.setHeight(boxWidth);
34582 getContainerWidth : function()
34584 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34587 layoutItems : function( isInstant )
34589 Roo.log(this.bricks);
34591 var items = Roo.apply([], this.bricks);
34593 if(this.isHorizontal){
34594 this._horizontalLayoutItems( items , isInstant );
34598 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34599 // this._verticalAlternativeLayoutItems( items , isInstant );
34603 this._verticalLayoutItems( items , isInstant );
34607 _verticalLayoutItems : function ( items , isInstant)
34609 if ( !items || !items.length ) {
34614 ['xs', 'xs', 'xs', 'tall'],
34615 ['xs', 'xs', 'tall'],
34616 ['xs', 'xs', 'sm'],
34617 ['xs', 'xs', 'xs'],
34623 ['sm', 'xs', 'xs'],
34627 ['tall', 'xs', 'xs', 'xs'],
34628 ['tall', 'xs', 'xs'],
34640 Roo.each(items, function(item, k){
34642 switch (item.size) {
34643 // these layouts take up a full box,
34654 boxes.push([item]);
34677 var filterPattern = function(box, length)
34685 var pattern = box.slice(0, length);
34689 Roo.each(pattern, function(i){
34690 format.push(i.size);
34693 Roo.each(standard, function(s){
34695 if(String(s) != String(format)){
34704 if(!match && length == 1){
34709 filterPattern(box, length - 1);
34713 queue.push(pattern);
34715 box = box.slice(length, box.length);
34717 filterPattern(box, 4);
34723 Roo.each(boxes, function(box, k){
34729 if(box.length == 1){
34734 filterPattern(box, 4);
34738 this._processVerticalLayoutQueue( queue, isInstant );
34742 // _verticalAlternativeLayoutItems : function( items , isInstant )
34744 // if ( !items || !items.length ) {
34748 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34752 _horizontalLayoutItems : function ( items , isInstant)
34754 if ( !items || !items.length || items.length < 3) {
34760 var eItems = items.slice(0, 3);
34762 items = items.slice(3, items.length);
34765 ['xs', 'xs', 'xs', 'wide'],
34766 ['xs', 'xs', 'wide'],
34767 ['xs', 'xs', 'sm'],
34768 ['xs', 'xs', 'xs'],
34774 ['sm', 'xs', 'xs'],
34778 ['wide', 'xs', 'xs', 'xs'],
34779 ['wide', 'xs', 'xs'],
34792 Roo.each(items, function(item, k){
34794 switch (item.size) {
34805 boxes.push([item]);
34829 var filterPattern = function(box, length)
34837 var pattern = box.slice(0, length);
34841 Roo.each(pattern, function(i){
34842 format.push(i.size);
34845 Roo.each(standard, function(s){
34847 if(String(s) != String(format)){
34856 if(!match && length == 1){
34861 filterPattern(box, length - 1);
34865 queue.push(pattern);
34867 box = box.slice(length, box.length);
34869 filterPattern(box, 4);
34875 Roo.each(boxes, function(box, k){
34881 if(box.length == 1){
34886 filterPattern(box, 4);
34893 var pos = this.el.getBox(true);
34897 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34899 var hit_end = false;
34901 Roo.each(queue, function(box){
34905 Roo.each(box, function(b){
34907 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34917 Roo.each(box, function(b){
34919 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34922 mx = Math.max(mx, b.x);
34926 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34930 Roo.each(box, function(b){
34932 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34946 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34949 /** Sets position of item in DOM
34950 * @param {Element} item
34951 * @param {Number} x - horizontal position
34952 * @param {Number} y - vertical position
34953 * @param {Boolean} isInstant - disables transitions
34955 _processVerticalLayoutQueue : function( queue, isInstant )
34957 var pos = this.el.getBox(true);
34962 for (var i = 0; i < this.cols; i++){
34966 Roo.each(queue, function(box, k){
34968 var col = k % this.cols;
34970 Roo.each(box, function(b,kk){
34972 b.el.position('absolute');
34974 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34975 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34977 if(b.size == 'md-left' || b.size == 'md-right'){
34978 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34979 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34982 b.el.setWidth(width);
34983 b.el.setHeight(height);
34985 b.el.select('iframe',true).setSize(width,height);
34989 for (var i = 0; i < this.cols; i++){
34991 if(maxY[i] < maxY[col]){
34996 col = Math.min(col, i);
35000 x = pos.x + col * (this.colWidth + this.padWidth);
35004 var positions = [];
35006 switch (box.length){
35008 positions = this.getVerticalOneBoxColPositions(x, y, box);
35011 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35014 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35017 positions = this.getVerticalFourBoxColPositions(x, y, box);
35023 Roo.each(box, function(b,kk){
35025 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35027 var sz = b.el.getSize();
35029 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35037 for (var i = 0; i < this.cols; i++){
35038 mY = Math.max(mY, maxY[i]);
35041 this.el.setHeight(mY - pos.y);
35045 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35047 // var pos = this.el.getBox(true);
35050 // var maxX = pos.right;
35052 // var maxHeight = 0;
35054 // Roo.each(items, function(item, k){
35058 // item.el.position('absolute');
35060 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35062 // item.el.setWidth(width);
35064 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35066 // item.el.setHeight(height);
35069 // item.el.setXY([x, y], isInstant ? false : true);
35071 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35074 // y = y + height + this.alternativePadWidth;
35076 // maxHeight = maxHeight + height + this.alternativePadWidth;
35080 // this.el.setHeight(maxHeight);
35084 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35086 var pos = this.el.getBox(true);
35091 var maxX = pos.right;
35093 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35095 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35097 Roo.each(queue, function(box, k){
35099 Roo.each(box, function(b, kk){
35101 b.el.position('absolute');
35103 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35104 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35106 if(b.size == 'md-left' || b.size == 'md-right'){
35107 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35108 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35111 b.el.setWidth(width);
35112 b.el.setHeight(height);
35120 var positions = [];
35122 switch (box.length){
35124 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35127 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35130 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35133 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35139 Roo.each(box, function(b,kk){
35141 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35143 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35151 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35153 Roo.each(eItems, function(b,k){
35155 b.size = (k == 0) ? 'sm' : 'xs';
35156 b.x = (k == 0) ? 2 : 1;
35157 b.y = (k == 0) ? 2 : 1;
35159 b.el.position('absolute');
35161 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35163 b.el.setWidth(width);
35165 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35167 b.el.setHeight(height);
35171 var positions = [];
35174 x : maxX - this.unitWidth * 2 - this.gutter,
35179 x : maxX - this.unitWidth,
35180 y : minY + (this.unitWidth + this.gutter) * 2
35184 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35188 Roo.each(eItems, function(b,k){
35190 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35196 getVerticalOneBoxColPositions : function(x, y, box)
35200 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35202 if(box[0].size == 'md-left'){
35206 if(box[0].size == 'md-right'){
35211 x : x + (this.unitWidth + this.gutter) * rand,
35218 getVerticalTwoBoxColPositions : function(x, y, box)
35222 if(box[0].size == 'xs'){
35226 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35230 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35244 x : x + (this.unitWidth + this.gutter) * 2,
35245 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35252 getVerticalThreeBoxColPositions : function(x, y, box)
35256 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35264 x : x + (this.unitWidth + this.gutter) * 1,
35269 x : x + (this.unitWidth + this.gutter) * 2,
35277 if(box[0].size == 'xs' && box[1].size == 'xs'){
35286 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35290 x : x + (this.unitWidth + this.gutter) * 1,
35304 x : x + (this.unitWidth + this.gutter) * 2,
35309 x : x + (this.unitWidth + this.gutter) * 2,
35310 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35317 getVerticalFourBoxColPositions : function(x, y, box)
35321 if(box[0].size == 'xs'){
35330 y : y + (this.unitHeight + this.gutter) * 1
35335 y : y + (this.unitHeight + this.gutter) * 2
35339 x : x + (this.unitWidth + this.gutter) * 1,
35353 x : x + (this.unitWidth + this.gutter) * 2,
35358 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35359 y : y + (this.unitHeight + this.gutter) * 1
35363 x : x + (this.unitWidth + this.gutter) * 2,
35364 y : y + (this.unitWidth + this.gutter) * 2
35371 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35375 if(box[0].size == 'md-left'){
35377 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35384 if(box[0].size == 'md-right'){
35386 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35387 y : minY + (this.unitWidth + this.gutter) * 1
35393 var rand = Math.floor(Math.random() * (4 - box[0].y));
35396 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35397 y : minY + (this.unitWidth + this.gutter) * rand
35404 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35408 if(box[0].size == 'xs'){
35411 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35416 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35417 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35425 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35430 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35431 y : minY + (this.unitWidth + this.gutter) * 2
35438 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35442 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35445 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35450 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35451 y : minY + (this.unitWidth + this.gutter) * 1
35455 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35456 y : minY + (this.unitWidth + this.gutter) * 2
35463 if(box[0].size == 'xs' && box[1].size == 'xs'){
35466 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35471 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35476 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35477 y : minY + (this.unitWidth + this.gutter) * 1
35485 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35490 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35491 y : minY + (this.unitWidth + this.gutter) * 2
35495 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35496 y : minY + (this.unitWidth + this.gutter) * 2
35503 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35507 if(box[0].size == 'xs'){
35510 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35515 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35520 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),
35525 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35526 y : minY + (this.unitWidth + this.gutter) * 1
35534 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35539 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35540 y : minY + (this.unitWidth + this.gutter) * 2
35544 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35545 y : minY + (this.unitWidth + this.gutter) * 2
35549 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),
35550 y : minY + (this.unitWidth + this.gutter) * 2
35558 * remove a Masonry Brick
35559 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35561 removeBrick : function(brick_id)
35567 for (var i = 0; i<this.bricks.length; i++) {
35568 if (this.bricks[i].id == brick_id) {
35569 this.bricks.splice(i,1);
35570 this.el.dom.removeChild(Roo.get(brick_id).dom);
35577 * adds a Masonry Brick
35578 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35580 addBrick : function(cfg)
35582 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35583 //this.register(cn);
35584 cn.parentId = this.id;
35585 cn.render(this.el);
35590 * register a Masonry Brick
35591 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35594 register : function(brick)
35596 this.bricks.push(brick);
35597 brick.masonryId = this.id;
35601 * clear all the Masonry Brick
35603 clearAll : function()
35606 //this.getChildContainer().dom.innerHTML = "";
35607 this.el.dom.innerHTML = '';
35610 getSelected : function()
35612 if (!this.selectedBrick) {
35616 return this.selectedBrick;
35620 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35624 * register a Masonry Layout
35625 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35628 register : function(layout)
35630 this.groups[layout.id] = layout;
35633 * fetch a Masonry Layout based on the masonry layout ID
35634 * @param {string} the masonry layout to add
35635 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35638 get: function(layout_id) {
35639 if (typeof(this.groups[layout_id]) == 'undefined') {
35642 return this.groups[layout_id] ;
35654 * http://masonry.desandro.com
35656 * The idea is to render all the bricks based on vertical width...
35658 * The original code extends 'outlayer' - we might need to use that....
35664 * @class Roo.bootstrap.LayoutMasonryAuto
35665 * @extends Roo.bootstrap.Component
35666 * Bootstrap Layout Masonry class
35669 * Create a new Element
35670 * @param {Object} config The config object
35673 Roo.bootstrap.LayoutMasonryAuto = function(config){
35674 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35677 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35680 * @cfg {Boolean} isFitWidth - resize the width..
35682 isFitWidth : false, // options..
35684 * @cfg {Boolean} isOriginLeft = left align?
35686 isOriginLeft : true,
35688 * @cfg {Boolean} isOriginTop = top align?
35690 isOriginTop : false,
35692 * @cfg {Boolean} isLayoutInstant = no animation?
35694 isLayoutInstant : false, // needed?
35696 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35698 isResizingContainer : true,
35700 * @cfg {Number} columnWidth width of the columns
35706 * @cfg {Number} maxCols maximum number of columns
35711 * @cfg {Number} padHeight padding below box..
35717 * @cfg {Boolean} isAutoInitial defalut true
35720 isAutoInitial : true,
35726 initialColumnWidth : 0,
35727 currentSize : null,
35729 colYs : null, // array.
35736 bricks: null, //CompositeElement
35737 cols : 0, // array?
35738 // element : null, // wrapped now this.el
35739 _isLayoutInited : null,
35742 getAutoCreate : function(){
35746 cls: 'blog-masonary-wrapper ' + this.cls,
35748 cls : 'mas-boxes masonary'
35755 getChildContainer: function( )
35757 if (this.boxesEl) {
35758 return this.boxesEl;
35761 this.boxesEl = this.el.select('.mas-boxes').first();
35763 return this.boxesEl;
35767 initEvents : function()
35771 if(this.isAutoInitial){
35772 Roo.log('hook children rendered');
35773 this.on('childrenrendered', function() {
35774 Roo.log('children rendered');
35781 initial : function()
35783 this.reloadItems();
35785 this.currentSize = this.el.getBox(true);
35787 /// was window resize... - let's see if this works..
35788 Roo.EventManager.onWindowResize(this.resize, this);
35790 if(!this.isAutoInitial){
35795 this.layout.defer(500,this);
35798 reloadItems: function()
35800 this.bricks = this.el.select('.masonry-brick', true);
35802 this.bricks.each(function(b) {
35803 //Roo.log(b.getSize());
35804 if (!b.attr('originalwidth')) {
35805 b.attr('originalwidth', b.getSize().width);
35810 Roo.log(this.bricks.elements.length);
35813 resize : function()
35816 var cs = this.el.getBox(true);
35818 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35819 Roo.log("no change in with or X");
35822 this.currentSize = cs;
35826 layout : function()
35829 this._resetLayout();
35830 //this._manageStamps();
35832 // don't animate first layout
35833 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35834 this.layoutItems( isInstant );
35836 // flag for initalized
35837 this._isLayoutInited = true;
35840 layoutItems : function( isInstant )
35842 //var items = this._getItemsForLayout( this.items );
35843 // original code supports filtering layout items.. we just ignore it..
35845 this._layoutItems( this.bricks , isInstant );
35847 this._postLayout();
35849 _layoutItems : function ( items , isInstant)
35851 //this.fireEvent( 'layout', this, items );
35854 if ( !items || !items.elements.length ) {
35855 // no items, emit event with empty array
35860 items.each(function(item) {
35861 Roo.log("layout item");
35863 // get x/y object from method
35864 var position = this._getItemLayoutPosition( item );
35866 position.item = item;
35867 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35868 queue.push( position );
35871 this._processLayoutQueue( queue );
35873 /** Sets position of item in DOM
35874 * @param {Element} item
35875 * @param {Number} x - horizontal position
35876 * @param {Number} y - vertical position
35877 * @param {Boolean} isInstant - disables transitions
35879 _processLayoutQueue : function( queue )
35881 for ( var i=0, len = queue.length; i < len; i++ ) {
35882 var obj = queue[i];
35883 obj.item.position('absolute');
35884 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35890 * Any logic you want to do after each layout,
35891 * i.e. size the container
35893 _postLayout : function()
35895 this.resizeContainer();
35898 resizeContainer : function()
35900 if ( !this.isResizingContainer ) {
35903 var size = this._getContainerSize();
35905 this.el.setSize(size.width,size.height);
35906 this.boxesEl.setSize(size.width,size.height);
35912 _resetLayout : function()
35914 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35915 this.colWidth = this.el.getWidth();
35916 //this.gutter = this.el.getWidth();
35918 this.measureColumns();
35924 this.colYs.push( 0 );
35930 measureColumns : function()
35932 this.getContainerWidth();
35933 // if columnWidth is 0, default to outerWidth of first item
35934 if ( !this.columnWidth ) {
35935 var firstItem = this.bricks.first();
35936 Roo.log(firstItem);
35937 this.columnWidth = this.containerWidth;
35938 if (firstItem && firstItem.attr('originalwidth') ) {
35939 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35941 // columnWidth fall back to item of first element
35942 Roo.log("set column width?");
35943 this.initialColumnWidth = this.columnWidth ;
35945 // if first elem has no width, default to size of container
35950 if (this.initialColumnWidth) {
35951 this.columnWidth = this.initialColumnWidth;
35956 // column width is fixed at the top - however if container width get's smaller we should
35959 // this bit calcs how man columns..
35961 var columnWidth = this.columnWidth += this.gutter;
35963 // calculate columns
35964 var containerWidth = this.containerWidth + this.gutter;
35966 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35967 // fix rounding errors, typically with gutters
35968 var excess = columnWidth - containerWidth % columnWidth;
35971 // if overshoot is less than a pixel, round up, otherwise floor it
35972 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35973 cols = Math[ mathMethod ]( cols );
35974 this.cols = Math.max( cols, 1 );
35975 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35977 // padding positioning..
35978 var totalColWidth = this.cols * this.columnWidth;
35979 var padavail = this.containerWidth - totalColWidth;
35980 // so for 2 columns - we need 3 'pads'
35982 var padNeeded = (1+this.cols) * this.padWidth;
35984 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35986 this.columnWidth += padExtra
35987 //this.padWidth = Math.floor(padavail / ( this.cols));
35989 // adjust colum width so that padding is fixed??
35991 // we have 3 columns ... total = width * 3
35992 // we have X left over... that should be used by
35994 //if (this.expandC) {
36002 getContainerWidth : function()
36004 /* // container is parent if fit width
36005 var container = this.isFitWidth ? this.element.parentNode : this.element;
36006 // check that this.size and size are there
36007 // IE8 triggers resize on body size change, so they might not be
36009 var size = getSize( container ); //FIXME
36010 this.containerWidth = size && size.innerWidth; //FIXME
36013 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36017 _getItemLayoutPosition : function( item ) // what is item?
36019 // we resize the item to our columnWidth..
36021 item.setWidth(this.columnWidth);
36022 item.autoBoxAdjust = false;
36024 var sz = item.getSize();
36026 // how many columns does this brick span
36027 var remainder = this.containerWidth % this.columnWidth;
36029 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36030 // round if off by 1 pixel, otherwise use ceil
36031 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36032 colSpan = Math.min( colSpan, this.cols );
36034 // normally this should be '1' as we dont' currently allow multi width columns..
36036 var colGroup = this._getColGroup( colSpan );
36037 // get the minimum Y value from the columns
36038 var minimumY = Math.min.apply( Math, colGroup );
36039 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36041 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36043 // position the brick
36045 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36046 y: this.currentSize.y + minimumY + this.padHeight
36050 // apply setHeight to necessary columns
36051 var setHeight = minimumY + sz.height + this.padHeight;
36052 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36054 var setSpan = this.cols + 1 - colGroup.length;
36055 for ( var i = 0; i < setSpan; i++ ) {
36056 this.colYs[ shortColIndex + i ] = setHeight ;
36063 * @param {Number} colSpan - number of columns the element spans
36064 * @returns {Array} colGroup
36066 _getColGroup : function( colSpan )
36068 if ( colSpan < 2 ) {
36069 // if brick spans only one column, use all the column Ys
36074 // how many different places could this brick fit horizontally
36075 var groupCount = this.cols + 1 - colSpan;
36076 // for each group potential horizontal position
36077 for ( var i = 0; i < groupCount; i++ ) {
36078 // make an array of colY values for that one group
36079 var groupColYs = this.colYs.slice( i, i + colSpan );
36080 // and get the max value of the array
36081 colGroup[i] = Math.max.apply( Math, groupColYs );
36086 _manageStamp : function( stamp )
36088 var stampSize = stamp.getSize();
36089 var offset = stamp.getBox();
36090 // get the columns that this stamp affects
36091 var firstX = this.isOriginLeft ? offset.x : offset.right;
36092 var lastX = firstX + stampSize.width;
36093 var firstCol = Math.floor( firstX / this.columnWidth );
36094 firstCol = Math.max( 0, firstCol );
36096 var lastCol = Math.floor( lastX / this.columnWidth );
36097 // lastCol should not go over if multiple of columnWidth #425
36098 lastCol -= lastX % this.columnWidth ? 0 : 1;
36099 lastCol = Math.min( this.cols - 1, lastCol );
36101 // set colYs to bottom of the stamp
36102 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36105 for ( var i = firstCol; i <= lastCol; i++ ) {
36106 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36111 _getContainerSize : function()
36113 this.maxY = Math.max.apply( Math, this.colYs );
36118 if ( this.isFitWidth ) {
36119 size.width = this._getContainerFitWidth();
36125 _getContainerFitWidth : function()
36127 var unusedCols = 0;
36128 // count unused columns
36131 if ( this.colYs[i] !== 0 ) {
36136 // fit container to columns that have been used
36137 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36140 needsResizeLayout : function()
36142 var previousWidth = this.containerWidth;
36143 this.getContainerWidth();
36144 return previousWidth !== this.containerWidth;
36159 * @class Roo.bootstrap.MasonryBrick
36160 * @extends Roo.bootstrap.Component
36161 * Bootstrap MasonryBrick class
36164 * Create a new MasonryBrick
36165 * @param {Object} config The config object
36168 Roo.bootstrap.MasonryBrick = function(config){
36170 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36172 Roo.bootstrap.MasonryBrick.register(this);
36178 * When a MasonryBrick is clcik
36179 * @param {Roo.bootstrap.MasonryBrick} this
36180 * @param {Roo.EventObject} e
36186 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36189 * @cfg {String} title
36193 * @cfg {String} html
36197 * @cfg {String} bgimage
36201 * @cfg {String} videourl
36205 * @cfg {String} cls
36209 * @cfg {String} href
36213 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36218 * @cfg {String} placetitle (center|bottom)
36223 * @cfg {Boolean} isFitContainer defalut true
36225 isFitContainer : true,
36228 * @cfg {Boolean} preventDefault defalut false
36230 preventDefault : false,
36233 * @cfg {Boolean} inverse defalut false
36235 maskInverse : false,
36237 getAutoCreate : function()
36239 if(!this.isFitContainer){
36240 return this.getSplitAutoCreate();
36243 var cls = 'masonry-brick masonry-brick-full';
36245 if(this.href.length){
36246 cls += ' masonry-brick-link';
36249 if(this.bgimage.length){
36250 cls += ' masonry-brick-image';
36253 if(this.maskInverse){
36254 cls += ' mask-inverse';
36257 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36258 cls += ' enable-mask';
36262 cls += ' masonry-' + this.size + '-brick';
36265 if(this.placetitle.length){
36267 switch (this.placetitle) {
36269 cls += ' masonry-center-title';
36272 cls += ' masonry-bottom-title';
36279 if(!this.html.length && !this.bgimage.length){
36280 cls += ' masonry-center-title';
36283 if(!this.html.length && this.bgimage.length){
36284 cls += ' masonry-bottom-title';
36289 cls += ' ' + this.cls;
36293 tag: (this.href.length) ? 'a' : 'div',
36298 cls: 'masonry-brick-mask'
36302 cls: 'masonry-brick-paragraph',
36308 if(this.href.length){
36309 cfg.href = this.href;
36312 var cn = cfg.cn[1].cn;
36314 if(this.title.length){
36317 cls: 'masonry-brick-title',
36322 if(this.html.length){
36325 cls: 'masonry-brick-text',
36330 if (!this.title.length && !this.html.length) {
36331 cfg.cn[1].cls += ' hide';
36334 if(this.bgimage.length){
36337 cls: 'masonry-brick-image-view',
36342 if(this.videourl.length){
36343 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36344 // youtube support only?
36347 cls: 'masonry-brick-image-view',
36350 allowfullscreen : true
36358 getSplitAutoCreate : function()
36360 var cls = 'masonry-brick masonry-brick-split';
36362 if(this.href.length){
36363 cls += ' masonry-brick-link';
36366 if(this.bgimage.length){
36367 cls += ' masonry-brick-image';
36371 cls += ' masonry-' + this.size + '-brick';
36374 switch (this.placetitle) {
36376 cls += ' masonry-center-title';
36379 cls += ' masonry-bottom-title';
36382 if(!this.bgimage.length){
36383 cls += ' masonry-center-title';
36386 if(this.bgimage.length){
36387 cls += ' masonry-bottom-title';
36393 cls += ' ' + this.cls;
36397 tag: (this.href.length) ? 'a' : 'div',
36402 cls: 'masonry-brick-split-head',
36406 cls: 'masonry-brick-paragraph',
36413 cls: 'masonry-brick-split-body',
36419 if(this.href.length){
36420 cfg.href = this.href;
36423 if(this.title.length){
36424 cfg.cn[0].cn[0].cn.push({
36426 cls: 'masonry-brick-title',
36431 if(this.html.length){
36432 cfg.cn[1].cn.push({
36434 cls: 'masonry-brick-text',
36439 if(this.bgimage.length){
36440 cfg.cn[0].cn.push({
36442 cls: 'masonry-brick-image-view',
36447 if(this.videourl.length){
36448 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36449 // youtube support only?
36450 cfg.cn[0].cn.cn.push({
36452 cls: 'masonry-brick-image-view',
36455 allowfullscreen : true
36462 initEvents: function()
36464 switch (this.size) {
36497 this.el.on('touchstart', this.onTouchStart, this);
36498 this.el.on('touchmove', this.onTouchMove, this);
36499 this.el.on('touchend', this.onTouchEnd, this);
36500 this.el.on('contextmenu', this.onContextMenu, this);
36502 this.el.on('mouseenter' ,this.enter, this);
36503 this.el.on('mouseleave', this.leave, this);
36504 this.el.on('click', this.onClick, this);
36507 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36508 this.parent().bricks.push(this);
36513 onClick: function(e, el)
36515 var time = this.endTimer - this.startTimer;
36516 // Roo.log(e.preventDefault());
36519 e.preventDefault();
36524 if(!this.preventDefault){
36528 e.preventDefault();
36530 if (this.activeClass != '') {
36531 this.selectBrick();
36534 this.fireEvent('click', this, e);
36537 enter: function(e, el)
36539 e.preventDefault();
36541 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36545 if(this.bgimage.length && this.html.length){
36546 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36550 leave: function(e, el)
36552 e.preventDefault();
36554 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36558 if(this.bgimage.length && this.html.length){
36559 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36563 onTouchStart: function(e, el)
36565 // e.preventDefault();
36567 this.touchmoved = false;
36569 if(!this.isFitContainer){
36573 if(!this.bgimage.length || !this.html.length){
36577 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36579 this.timer = new Date().getTime();
36583 onTouchMove: function(e, el)
36585 this.touchmoved = true;
36588 onContextMenu : function(e,el)
36590 e.preventDefault();
36591 e.stopPropagation();
36595 onTouchEnd: function(e, el)
36597 // e.preventDefault();
36599 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36606 if(!this.bgimage.length || !this.html.length){
36608 if(this.href.length){
36609 window.location.href = this.href;
36615 if(!this.isFitContainer){
36619 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36621 window.location.href = this.href;
36624 //selection on single brick only
36625 selectBrick : function() {
36627 if (!this.parentId) {
36631 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36632 var index = m.selectedBrick.indexOf(this.id);
36635 m.selectedBrick.splice(index,1);
36636 this.el.removeClass(this.activeClass);
36640 for(var i = 0; i < m.selectedBrick.length; i++) {
36641 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36642 b.el.removeClass(b.activeClass);
36645 m.selectedBrick = [];
36647 m.selectedBrick.push(this.id);
36648 this.el.addClass(this.activeClass);
36652 isSelected : function(){
36653 return this.el.hasClass(this.activeClass);
36658 Roo.apply(Roo.bootstrap.MasonryBrick, {
36661 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36663 * register a Masonry Brick
36664 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36667 register : function(brick)
36669 //this.groups[brick.id] = brick;
36670 this.groups.add(brick.id, brick);
36673 * fetch a masonry brick based on the masonry brick ID
36674 * @param {string} the masonry brick to add
36675 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36678 get: function(brick_id)
36680 // if (typeof(this.groups[brick_id]) == 'undefined') {
36683 // return this.groups[brick_id] ;
36685 if(this.groups.key(brick_id)) {
36686 return this.groups.key(brick_id);
36704 * @class Roo.bootstrap.Brick
36705 * @extends Roo.bootstrap.Component
36706 * Bootstrap Brick class
36709 * Create a new Brick
36710 * @param {Object} config The config object
36713 Roo.bootstrap.Brick = function(config){
36714 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36720 * When a Brick is click
36721 * @param {Roo.bootstrap.Brick} this
36722 * @param {Roo.EventObject} e
36728 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36731 * @cfg {String} title
36735 * @cfg {String} html
36739 * @cfg {String} bgimage
36743 * @cfg {String} cls
36747 * @cfg {String} href
36751 * @cfg {String} video
36755 * @cfg {Boolean} square
36759 getAutoCreate : function()
36761 var cls = 'roo-brick';
36763 if(this.href.length){
36764 cls += ' roo-brick-link';
36767 if(this.bgimage.length){
36768 cls += ' roo-brick-image';
36771 if(!this.html.length && !this.bgimage.length){
36772 cls += ' roo-brick-center-title';
36775 if(!this.html.length && this.bgimage.length){
36776 cls += ' roo-brick-bottom-title';
36780 cls += ' ' + this.cls;
36784 tag: (this.href.length) ? 'a' : 'div',
36789 cls: 'roo-brick-paragraph',
36795 if(this.href.length){
36796 cfg.href = this.href;
36799 var cn = cfg.cn[0].cn;
36801 if(this.title.length){
36804 cls: 'roo-brick-title',
36809 if(this.html.length){
36812 cls: 'roo-brick-text',
36819 if(this.bgimage.length){
36822 cls: 'roo-brick-image-view',
36830 initEvents: function()
36832 if(this.title.length || this.html.length){
36833 this.el.on('mouseenter' ,this.enter, this);
36834 this.el.on('mouseleave', this.leave, this);
36837 Roo.EventManager.onWindowResize(this.resize, this);
36839 if(this.bgimage.length){
36840 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36841 this.imageEl.on('load', this.onImageLoad, this);
36848 onImageLoad : function()
36853 resize : function()
36855 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36857 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36859 if(this.bgimage.length){
36860 var image = this.el.select('.roo-brick-image-view', true).first();
36862 image.setWidth(paragraph.getWidth());
36865 image.setHeight(paragraph.getWidth());
36868 this.el.setHeight(image.getHeight());
36869 paragraph.setHeight(image.getHeight());
36875 enter: function(e, el)
36877 e.preventDefault();
36879 if(this.bgimage.length){
36880 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36881 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36885 leave: function(e, el)
36887 e.preventDefault();
36889 if(this.bgimage.length){
36890 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36891 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36906 * @class Roo.bootstrap.NumberField
36907 * @extends Roo.bootstrap.Input
36908 * Bootstrap NumberField class
36914 * Create a new NumberField
36915 * @param {Object} config The config object
36918 Roo.bootstrap.NumberField = function(config){
36919 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36922 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36925 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36927 allowDecimals : true,
36929 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36931 decimalSeparator : ".",
36933 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36935 decimalPrecision : 2,
36937 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36939 allowNegative : true,
36942 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36946 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36948 minValue : Number.NEGATIVE_INFINITY,
36950 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36952 maxValue : Number.MAX_VALUE,
36954 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36956 minText : "The minimum value for this field is {0}",
36958 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36960 maxText : "The maximum value for this field is {0}",
36962 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36963 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36965 nanText : "{0} is not a valid number",
36967 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36969 thousandsDelimiter : false,
36971 * @cfg {String} valueAlign alignment of value
36973 valueAlign : "left",
36975 getAutoCreate : function()
36977 var hiddenInput = {
36981 cls: 'hidden-number-input'
36985 hiddenInput.name = this.name;
36990 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36992 this.name = hiddenInput.name;
36994 if(cfg.cn.length > 0) {
36995 cfg.cn.push(hiddenInput);
37002 initEvents : function()
37004 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37006 var allowed = "0123456789";
37008 if(this.allowDecimals){
37009 allowed += this.decimalSeparator;
37012 if(this.allowNegative){
37016 if(this.thousandsDelimiter) {
37020 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37022 var keyPress = function(e){
37024 var k = e.getKey();
37026 var c = e.getCharCode();
37029 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37030 allowed.indexOf(String.fromCharCode(c)) === -1
37036 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37040 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37045 this.el.on("keypress", keyPress, this);
37048 validateValue : function(value)
37051 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37055 var num = this.parseValue(value);
37058 this.markInvalid(String.format(this.nanText, value));
37062 if(num < this.minValue){
37063 this.markInvalid(String.format(this.minText, this.minValue));
37067 if(num > this.maxValue){
37068 this.markInvalid(String.format(this.maxText, this.maxValue));
37075 getValue : function()
37077 var v = this.hiddenEl().getValue();
37079 return this.fixPrecision(this.parseValue(v));
37082 parseValue : function(value)
37084 if(this.thousandsDelimiter) {
37086 r = new RegExp(",", "g");
37087 value = value.replace(r, "");
37090 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37091 return isNaN(value) ? '' : value;
37094 fixPrecision : function(value)
37096 if(this.thousandsDelimiter) {
37098 r = new RegExp(",", "g");
37099 value = value.replace(r, "");
37102 var nan = isNaN(value);
37104 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37105 return nan ? '' : value;
37107 return parseFloat(value).toFixed(this.decimalPrecision);
37110 setValue : function(v)
37112 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37118 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37120 this.inputEl().dom.value = (v == '') ? '' :
37121 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37123 if(!this.allowZero && v === '0') {
37124 this.hiddenEl().dom.value = '';
37125 this.inputEl().dom.value = '';
37132 decimalPrecisionFcn : function(v)
37134 return Math.floor(v);
37137 beforeBlur : function()
37139 var v = this.parseValue(this.getRawValue());
37141 if(v || v === 0 || v === ''){
37146 hiddenEl : function()
37148 return this.el.select('input.hidden-number-input',true).first();
37160 * @class Roo.bootstrap.DocumentSlider
37161 * @extends Roo.bootstrap.Component
37162 * Bootstrap DocumentSlider class
37165 * Create a new DocumentViewer
37166 * @param {Object} config The config object
37169 Roo.bootstrap.DocumentSlider = function(config){
37170 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37177 * Fire after initEvent
37178 * @param {Roo.bootstrap.DocumentSlider} this
37183 * Fire after update
37184 * @param {Roo.bootstrap.DocumentSlider} this
37190 * @param {Roo.bootstrap.DocumentSlider} this
37196 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37202 getAutoCreate : function()
37206 cls : 'roo-document-slider',
37210 cls : 'roo-document-slider-header',
37214 cls : 'roo-document-slider-header-title'
37220 cls : 'roo-document-slider-body',
37224 cls : 'roo-document-slider-prev',
37228 cls : 'fa fa-chevron-left'
37234 cls : 'roo-document-slider-thumb',
37238 cls : 'roo-document-slider-image'
37244 cls : 'roo-document-slider-next',
37248 cls : 'fa fa-chevron-right'
37260 initEvents : function()
37262 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37263 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37265 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37266 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37268 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37269 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37271 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37272 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37274 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37275 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37277 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37278 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37280 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37281 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37283 this.thumbEl.on('click', this.onClick, this);
37285 this.prevIndicator.on('click', this.prev, this);
37287 this.nextIndicator.on('click', this.next, this);
37291 initial : function()
37293 if(this.files.length){
37294 this.indicator = 1;
37298 this.fireEvent('initial', this);
37301 update : function()
37303 this.imageEl.attr('src', this.files[this.indicator - 1]);
37305 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37307 this.prevIndicator.show();
37309 if(this.indicator == 1){
37310 this.prevIndicator.hide();
37313 this.nextIndicator.show();
37315 if(this.indicator == this.files.length){
37316 this.nextIndicator.hide();
37319 this.thumbEl.scrollTo('top');
37321 this.fireEvent('update', this);
37324 onClick : function(e)
37326 e.preventDefault();
37328 this.fireEvent('click', this);
37333 e.preventDefault();
37335 this.indicator = Math.max(1, this.indicator - 1);
37342 e.preventDefault();
37344 this.indicator = Math.min(this.files.length, this.indicator + 1);
37358 * @class Roo.bootstrap.RadioSet
37359 * @extends Roo.bootstrap.Input
37360 * Bootstrap RadioSet class
37361 * @cfg {String} indicatorpos (left|right) default left
37362 * @cfg {Boolean} inline (true|false) inline the element (default true)
37363 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37365 * Create a new RadioSet
37366 * @param {Object} config The config object
37369 Roo.bootstrap.RadioSet = function(config){
37371 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37375 Roo.bootstrap.RadioSet.register(this);
37380 * Fires when the element is checked or unchecked.
37381 * @param {Roo.bootstrap.RadioSet} this This radio
37382 * @param {Roo.bootstrap.Radio} item The checked item
37387 * Fires when the element is click.
37388 * @param {Roo.bootstrap.RadioSet} this This radio set
37389 * @param {Roo.bootstrap.Radio} item The checked item
37390 * @param {Roo.EventObject} e The event object
37397 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37405 indicatorpos : 'left',
37407 getAutoCreate : function()
37411 cls : 'roo-radio-set-label',
37415 html : this.fieldLabel
37419 if (Roo.bootstrap.version == 3) {
37422 if(this.indicatorpos == 'left'){
37425 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37426 tooltip : 'This field is required'
37431 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37432 tooltip : 'This field is required'
37438 cls : 'roo-radio-set-items'
37441 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37443 if (align === 'left' && this.fieldLabel.length) {
37446 cls : "roo-radio-set-right",
37452 if(this.labelWidth > 12){
37453 label.style = "width: " + this.labelWidth + 'px';
37456 if(this.labelWidth < 13 && this.labelmd == 0){
37457 this.labelmd = this.labelWidth;
37460 if(this.labellg > 0){
37461 label.cls += ' col-lg-' + this.labellg;
37462 items.cls += ' col-lg-' + (12 - this.labellg);
37465 if(this.labelmd > 0){
37466 label.cls += ' col-md-' + this.labelmd;
37467 items.cls += ' col-md-' + (12 - this.labelmd);
37470 if(this.labelsm > 0){
37471 label.cls += ' col-sm-' + this.labelsm;
37472 items.cls += ' col-sm-' + (12 - this.labelsm);
37475 if(this.labelxs > 0){
37476 label.cls += ' col-xs-' + this.labelxs;
37477 items.cls += ' col-xs-' + (12 - this.labelxs);
37483 cls : 'roo-radio-set',
37487 cls : 'roo-radio-set-input',
37490 value : this.value ? this.value : ''
37497 if(this.weight.length){
37498 cfg.cls += ' roo-radio-' + this.weight;
37502 cfg.cls += ' roo-radio-set-inline';
37506 ['xs','sm','md','lg'].map(function(size){
37507 if (settings[size]) {
37508 cfg.cls += ' col-' + size + '-' + settings[size];
37516 initEvents : function()
37518 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37519 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37521 if(!this.fieldLabel.length){
37522 this.labelEl.hide();
37525 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37526 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37528 this.indicator = this.indicatorEl();
37530 if(this.indicator){
37531 this.indicator.addClass('invisible');
37534 this.originalValue = this.getValue();
37538 inputEl: function ()
37540 return this.el.select('.roo-radio-set-input', true).first();
37543 getChildContainer : function()
37545 return this.itemsEl;
37548 register : function(item)
37550 this.radioes.push(item);
37554 validate : function()
37556 if(this.getVisibilityEl().hasClass('hidden')){
37562 Roo.each(this.radioes, function(i){
37571 if(this.allowBlank) {
37575 if(this.disabled || valid){
37580 this.markInvalid();
37585 markValid : function()
37587 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37588 this.indicatorEl().removeClass('visible');
37589 this.indicatorEl().addClass('invisible');
37593 if (Roo.bootstrap.version == 3) {
37594 this.el.removeClass([this.invalidClass, this.validClass]);
37595 this.el.addClass(this.validClass);
37597 this.el.removeClass(['is-invalid','is-valid']);
37598 this.el.addClass(['is-valid']);
37600 this.fireEvent('valid', this);
37603 markInvalid : function(msg)
37605 if(this.allowBlank || this.disabled){
37609 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37610 this.indicatorEl().removeClass('invisible');
37611 this.indicatorEl().addClass('visible');
37613 if (Roo.bootstrap.version == 3) {
37614 this.el.removeClass([this.invalidClass, this.validClass]);
37615 this.el.addClass(this.invalidClass);
37617 this.el.removeClass(['is-invalid','is-valid']);
37618 this.el.addClass(['is-invalid']);
37621 this.fireEvent('invalid', this, msg);
37625 setValue : function(v, suppressEvent)
37627 if(this.value === v){
37634 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37637 Roo.each(this.radioes, function(i){
37639 i.el.removeClass('checked');
37642 Roo.each(this.radioes, function(i){
37644 if(i.value === v || i.value.toString() === v.toString()){
37646 i.el.addClass('checked');
37648 if(suppressEvent !== true){
37649 this.fireEvent('check', this, i);
37660 clearInvalid : function(){
37662 if(!this.el || this.preventMark){
37666 this.el.removeClass([this.invalidClass]);
37668 this.fireEvent('valid', this);
37673 Roo.apply(Roo.bootstrap.RadioSet, {
37677 register : function(set)
37679 this.groups[set.name] = set;
37682 get: function(name)
37684 if (typeof(this.groups[name]) == 'undefined') {
37688 return this.groups[name] ;
37694 * Ext JS Library 1.1.1
37695 * Copyright(c) 2006-2007, Ext JS, LLC.
37697 * Originally Released Under LGPL - original licence link has changed is not relivant.
37700 * <script type="text/javascript">
37705 * @class Roo.bootstrap.SplitBar
37706 * @extends Roo.util.Observable
37707 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37711 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37712 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37713 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37714 split.minSize = 100;
37715 split.maxSize = 600;
37716 split.animate = true;
37717 split.on('moved', splitterMoved);
37720 * Create a new SplitBar
37721 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37722 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37723 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37724 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37725 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37726 position of the SplitBar).
37728 Roo.bootstrap.SplitBar = function(cfg){
37733 // dragElement : elm
37734 // resizingElement: el,
37736 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37737 // placement : Roo.bootstrap.SplitBar.LEFT ,
37738 // existingProxy ???
37741 this.el = Roo.get(cfg.dragElement, true);
37742 this.el.dom.unselectable = "on";
37744 this.resizingEl = Roo.get(cfg.resizingElement, true);
37748 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37749 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37752 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37755 * The minimum size of the resizing element. (Defaults to 0)
37761 * The maximum size of the resizing element. (Defaults to 2000)
37764 this.maxSize = 2000;
37767 * Whether to animate the transition to the new size
37770 this.animate = false;
37773 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37776 this.useShim = false;
37781 if(!cfg.existingProxy){
37783 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37785 this.proxy = Roo.get(cfg.existingProxy).dom;
37788 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37791 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37794 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37797 this.dragSpecs = {};
37800 * @private The adapter to use to positon and resize elements
37802 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37803 this.adapter.init(this);
37805 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37807 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37808 this.el.addClass("roo-splitbar-h");
37811 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37812 this.el.addClass("roo-splitbar-v");
37818 * Fires when the splitter is moved (alias for {@link #event-moved})
37819 * @param {Roo.bootstrap.SplitBar} this
37820 * @param {Number} newSize the new width or height
37825 * Fires when the splitter is moved
37826 * @param {Roo.bootstrap.SplitBar} this
37827 * @param {Number} newSize the new width or height
37831 * @event beforeresize
37832 * Fires before the splitter is dragged
37833 * @param {Roo.bootstrap.SplitBar} this
37835 "beforeresize" : true,
37837 "beforeapply" : true
37840 Roo.util.Observable.call(this);
37843 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37844 onStartProxyDrag : function(x, y){
37845 this.fireEvent("beforeresize", this);
37847 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37849 o.enableDisplayMode("block");
37850 // all splitbars share the same overlay
37851 Roo.bootstrap.SplitBar.prototype.overlay = o;
37853 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37854 this.overlay.show();
37855 Roo.get(this.proxy).setDisplayed("block");
37856 var size = this.adapter.getElementSize(this);
37857 this.activeMinSize = this.getMinimumSize();;
37858 this.activeMaxSize = this.getMaximumSize();;
37859 var c1 = size - this.activeMinSize;
37860 var c2 = Math.max(this.activeMaxSize - size, 0);
37861 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37862 this.dd.resetConstraints();
37863 this.dd.setXConstraint(
37864 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37865 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37867 this.dd.setYConstraint(0, 0);
37869 this.dd.resetConstraints();
37870 this.dd.setXConstraint(0, 0);
37871 this.dd.setYConstraint(
37872 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37873 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37876 this.dragSpecs.startSize = size;
37877 this.dragSpecs.startPoint = [x, y];
37878 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37882 * @private Called after the drag operation by the DDProxy
37884 onEndProxyDrag : function(e){
37885 Roo.get(this.proxy).setDisplayed(false);
37886 var endPoint = Roo.lib.Event.getXY(e);
37888 this.overlay.hide();
37891 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37892 newSize = this.dragSpecs.startSize +
37893 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37894 endPoint[0] - this.dragSpecs.startPoint[0] :
37895 this.dragSpecs.startPoint[0] - endPoint[0]
37898 newSize = this.dragSpecs.startSize +
37899 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37900 endPoint[1] - this.dragSpecs.startPoint[1] :
37901 this.dragSpecs.startPoint[1] - endPoint[1]
37904 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37905 if(newSize != this.dragSpecs.startSize){
37906 if(this.fireEvent('beforeapply', this, newSize) !== false){
37907 this.adapter.setElementSize(this, newSize);
37908 this.fireEvent("moved", this, newSize);
37909 this.fireEvent("resize", this, newSize);
37915 * Get the adapter this SplitBar uses
37916 * @return The adapter object
37918 getAdapter : function(){
37919 return this.adapter;
37923 * Set the adapter this SplitBar uses
37924 * @param {Object} adapter A SplitBar adapter object
37926 setAdapter : function(adapter){
37927 this.adapter = adapter;
37928 this.adapter.init(this);
37932 * Gets the minimum size for the resizing element
37933 * @return {Number} The minimum size
37935 getMinimumSize : function(){
37936 return this.minSize;
37940 * Sets the minimum size for the resizing element
37941 * @param {Number} minSize The minimum size
37943 setMinimumSize : function(minSize){
37944 this.minSize = minSize;
37948 * Gets the maximum size for the resizing element
37949 * @return {Number} The maximum size
37951 getMaximumSize : function(){
37952 return this.maxSize;
37956 * Sets the maximum size for the resizing element
37957 * @param {Number} maxSize The maximum size
37959 setMaximumSize : function(maxSize){
37960 this.maxSize = maxSize;
37964 * Sets the initialize size for the resizing element
37965 * @param {Number} size The initial size
37967 setCurrentSize : function(size){
37968 var oldAnimate = this.animate;
37969 this.animate = false;
37970 this.adapter.setElementSize(this, size);
37971 this.animate = oldAnimate;
37975 * Destroy this splitbar.
37976 * @param {Boolean} removeEl True to remove the element
37978 destroy : function(removeEl){
37980 this.shim.remove();
37983 this.proxy.parentNode.removeChild(this.proxy);
37991 * @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.
37993 Roo.bootstrap.SplitBar.createProxy = function(dir){
37994 var proxy = new Roo.Element(document.createElement("div"));
37995 proxy.unselectable();
37996 var cls = 'roo-splitbar-proxy';
37997 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37998 document.body.appendChild(proxy.dom);
38003 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38004 * Default Adapter. It assumes the splitter and resizing element are not positioned
38005 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38007 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38010 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38011 // do nothing for now
38012 init : function(s){
38016 * Called before drag operations to get the current size of the resizing element.
38017 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38019 getElementSize : function(s){
38020 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38021 return s.resizingEl.getWidth();
38023 return s.resizingEl.getHeight();
38028 * Called after drag operations to set the size of the resizing element.
38029 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38030 * @param {Number} newSize The new size to set
38031 * @param {Function} onComplete A function to be invoked when resizing is complete
38033 setElementSize : function(s, newSize, onComplete){
38034 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38036 s.resizingEl.setWidth(newSize);
38038 onComplete(s, newSize);
38041 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38046 s.resizingEl.setHeight(newSize);
38048 onComplete(s, newSize);
38051 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38058 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38059 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38060 * Adapter that moves the splitter element to align with the resized sizing element.
38061 * Used with an absolute positioned SplitBar.
38062 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38063 * document.body, make sure you assign an id to the body element.
38065 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38066 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38067 this.container = Roo.get(container);
38070 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38071 init : function(s){
38072 this.basic.init(s);
38075 getElementSize : function(s){
38076 return this.basic.getElementSize(s);
38079 setElementSize : function(s, newSize, onComplete){
38080 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38083 moveSplitter : function(s){
38084 var yes = Roo.bootstrap.SplitBar;
38085 switch(s.placement){
38087 s.el.setX(s.resizingEl.getRight());
38090 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38093 s.el.setY(s.resizingEl.getBottom());
38096 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38103 * Orientation constant - Create a vertical SplitBar
38107 Roo.bootstrap.SplitBar.VERTICAL = 1;
38110 * Orientation constant - Create a horizontal SplitBar
38114 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38117 * Placement constant - The resizing element is to the left of the splitter element
38121 Roo.bootstrap.SplitBar.LEFT = 1;
38124 * Placement constant - The resizing element is to the right of the splitter element
38128 Roo.bootstrap.SplitBar.RIGHT = 2;
38131 * Placement constant - The resizing element is positioned above the splitter element
38135 Roo.bootstrap.SplitBar.TOP = 3;
38138 * Placement constant - The resizing element is positioned under splitter element
38142 Roo.bootstrap.SplitBar.BOTTOM = 4;
38143 Roo.namespace("Roo.bootstrap.layout");/*
38145 * Ext JS Library 1.1.1
38146 * Copyright(c) 2006-2007, Ext JS, LLC.
38148 * Originally Released Under LGPL - original licence link has changed is not relivant.
38151 * <script type="text/javascript">
38155 * @class Roo.bootstrap.layout.Manager
38156 * @extends Roo.bootstrap.Component
38157 * Base class for layout managers.
38159 Roo.bootstrap.layout.Manager = function(config)
38161 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38167 /** false to disable window resize monitoring @type Boolean */
38168 this.monitorWindowResize = true;
38173 * Fires when a layout is performed.
38174 * @param {Roo.LayoutManager} this
38178 * @event regionresized
38179 * Fires when the user resizes a region.
38180 * @param {Roo.LayoutRegion} region The resized region
38181 * @param {Number} newSize The new size (width for east/west, height for north/south)
38183 "regionresized" : true,
38185 * @event regioncollapsed
38186 * Fires when a region is collapsed.
38187 * @param {Roo.LayoutRegion} region The collapsed region
38189 "regioncollapsed" : true,
38191 * @event regionexpanded
38192 * Fires when a region is expanded.
38193 * @param {Roo.LayoutRegion} region The expanded region
38195 "regionexpanded" : true
38197 this.updating = false;
38200 this.el = Roo.get(config.el);
38206 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38211 monitorWindowResize : true,
38217 onRender : function(ct, position)
38220 this.el = Roo.get(ct);
38223 //this.fireEvent('render',this);
38227 initEvents: function()
38231 // ie scrollbar fix
38232 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38233 document.body.scroll = "no";
38234 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38235 this.el.position('relative');
38237 this.id = this.el.id;
38238 this.el.addClass("roo-layout-container");
38239 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38240 if(this.el.dom != document.body ) {
38241 this.el.on('resize', this.layout,this);
38242 this.el.on('show', this.layout,this);
38248 * Returns true if this layout is currently being updated
38249 * @return {Boolean}
38251 isUpdating : function(){
38252 return this.updating;
38256 * Suspend the LayoutManager from doing auto-layouts while
38257 * making multiple add or remove calls
38259 beginUpdate : function(){
38260 this.updating = true;
38264 * Restore auto-layouts and optionally disable the manager from performing a layout
38265 * @param {Boolean} noLayout true to disable a layout update
38267 endUpdate : function(noLayout){
38268 this.updating = false;
38274 layout: function(){
38278 onRegionResized : function(region, newSize){
38279 this.fireEvent("regionresized", region, newSize);
38283 onRegionCollapsed : function(region){
38284 this.fireEvent("regioncollapsed", region);
38287 onRegionExpanded : function(region){
38288 this.fireEvent("regionexpanded", region);
38292 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38293 * performs box-model adjustments.
38294 * @return {Object} The size as an object {width: (the width), height: (the height)}
38296 getViewSize : function()
38299 if(this.el.dom != document.body){
38300 size = this.el.getSize();
38302 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38304 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38305 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38310 * Returns the Element this layout is bound to.
38311 * @return {Roo.Element}
38313 getEl : function(){
38318 * Returns the specified region.
38319 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38320 * @return {Roo.LayoutRegion}
38322 getRegion : function(target){
38323 return this.regions[target.toLowerCase()];
38326 onWindowResize : function(){
38327 if(this.monitorWindowResize){
38334 * Ext JS Library 1.1.1
38335 * Copyright(c) 2006-2007, Ext JS, LLC.
38337 * Originally Released Under LGPL - original licence link has changed is not relivant.
38340 * <script type="text/javascript">
38343 * @class Roo.bootstrap.layout.Border
38344 * @extends Roo.bootstrap.layout.Manager
38345 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38346 * please see: examples/bootstrap/nested.html<br><br>
38348 <b>The container the layout is rendered into can be either the body element or any other element.
38349 If it is not the body element, the container needs to either be an absolute positioned element,
38350 or you will need to add "position:relative" to the css of the container. You will also need to specify
38351 the container size if it is not the body element.</b>
38354 * Create a new Border
38355 * @param {Object} config Configuration options
38357 Roo.bootstrap.layout.Border = function(config){
38358 config = config || {};
38359 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38363 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38364 if(config[region]){
38365 config[region].region = region;
38366 this.addRegion(config[region]);
38372 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38374 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38376 parent : false, // this might point to a 'nest' or a ???
38379 * Creates and adds a new region if it doesn't already exist.
38380 * @param {String} target The target region key (north, south, east, west or center).
38381 * @param {Object} config The regions config object
38382 * @return {BorderLayoutRegion} The new region
38384 addRegion : function(config)
38386 if(!this.regions[config.region]){
38387 var r = this.factory(config);
38388 this.bindRegion(r);
38390 return this.regions[config.region];
38394 bindRegion : function(r){
38395 this.regions[r.config.region] = r;
38397 r.on("visibilitychange", this.layout, this);
38398 r.on("paneladded", this.layout, this);
38399 r.on("panelremoved", this.layout, this);
38400 r.on("invalidated", this.layout, this);
38401 r.on("resized", this.onRegionResized, this);
38402 r.on("collapsed", this.onRegionCollapsed, this);
38403 r.on("expanded", this.onRegionExpanded, this);
38407 * Performs a layout update.
38409 layout : function()
38411 if(this.updating) {
38415 // render all the rebions if they have not been done alreayd?
38416 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38417 if(this.regions[region] && !this.regions[region].bodyEl){
38418 this.regions[region].onRender(this.el)
38422 var size = this.getViewSize();
38423 var w = size.width;
38424 var h = size.height;
38429 //var x = 0, y = 0;
38431 var rs = this.regions;
38432 var north = rs["north"];
38433 var south = rs["south"];
38434 var west = rs["west"];
38435 var east = rs["east"];
38436 var center = rs["center"];
38437 //if(this.hideOnLayout){ // not supported anymore
38438 //c.el.setStyle("display", "none");
38440 if(north && north.isVisible()){
38441 var b = north.getBox();
38442 var m = north.getMargins();
38443 b.width = w - (m.left+m.right);
38446 centerY = b.height + b.y + m.bottom;
38447 centerH -= centerY;
38448 north.updateBox(this.safeBox(b));
38450 if(south && south.isVisible()){
38451 var b = south.getBox();
38452 var m = south.getMargins();
38453 b.width = w - (m.left+m.right);
38455 var totalHeight = (b.height + m.top + m.bottom);
38456 b.y = h - totalHeight + m.top;
38457 centerH -= totalHeight;
38458 south.updateBox(this.safeBox(b));
38460 if(west && west.isVisible()){
38461 var b = west.getBox();
38462 var m = west.getMargins();
38463 b.height = centerH - (m.top+m.bottom);
38465 b.y = centerY + m.top;
38466 var totalWidth = (b.width + m.left + m.right);
38467 centerX += totalWidth;
38468 centerW -= totalWidth;
38469 west.updateBox(this.safeBox(b));
38471 if(east && east.isVisible()){
38472 var b = east.getBox();
38473 var m = east.getMargins();
38474 b.height = centerH - (m.top+m.bottom);
38475 var totalWidth = (b.width + m.left + m.right);
38476 b.x = w - totalWidth + m.left;
38477 b.y = centerY + m.top;
38478 centerW -= totalWidth;
38479 east.updateBox(this.safeBox(b));
38482 var m = center.getMargins();
38484 x: centerX + m.left,
38485 y: centerY + m.top,
38486 width: centerW - (m.left+m.right),
38487 height: centerH - (m.top+m.bottom)
38489 //if(this.hideOnLayout){
38490 //center.el.setStyle("display", "block");
38492 center.updateBox(this.safeBox(centerBox));
38495 this.fireEvent("layout", this);
38499 safeBox : function(box){
38500 box.width = Math.max(0, box.width);
38501 box.height = Math.max(0, box.height);
38506 * Adds a ContentPanel (or subclass) to this layout.
38507 * @param {String} target The target region key (north, south, east, west or center).
38508 * @param {Roo.ContentPanel} panel The panel to add
38509 * @return {Roo.ContentPanel} The added panel
38511 add : function(target, panel){
38513 target = target.toLowerCase();
38514 return this.regions[target].add(panel);
38518 * Remove a ContentPanel (or subclass) to this layout.
38519 * @param {String} target The target region key (north, south, east, west or center).
38520 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38521 * @return {Roo.ContentPanel} The removed panel
38523 remove : function(target, panel){
38524 target = target.toLowerCase();
38525 return this.regions[target].remove(panel);
38529 * Searches all regions for a panel with the specified id
38530 * @param {String} panelId
38531 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38533 findPanel : function(panelId){
38534 var rs = this.regions;
38535 for(var target in rs){
38536 if(typeof rs[target] != "function"){
38537 var p = rs[target].getPanel(panelId);
38547 * Searches all regions for a panel with the specified id and activates (shows) it.
38548 * @param {String/ContentPanel} panelId The panels id or the panel itself
38549 * @return {Roo.ContentPanel} The shown panel or null
38551 showPanel : function(panelId) {
38552 var rs = this.regions;
38553 for(var target in rs){
38554 var r = rs[target];
38555 if(typeof r != "function"){
38556 if(r.hasPanel(panelId)){
38557 return r.showPanel(panelId);
38565 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38566 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38569 restoreState : function(provider){
38571 provider = Roo.state.Manager;
38573 var sm = new Roo.LayoutStateManager();
38574 sm.init(this, provider);
38580 * Adds a xtype elements to the layout.
38584 xtype : 'ContentPanel',
38591 xtype : 'NestedLayoutPanel',
38597 items : [ ... list of content panels or nested layout panels.. ]
38601 * @param {Object} cfg Xtype definition of item to add.
38603 addxtype : function(cfg)
38605 // basically accepts a pannel...
38606 // can accept a layout region..!?!?
38607 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38610 // theory? children can only be panels??
38612 //if (!cfg.xtype.match(/Panel$/)) {
38617 if (typeof(cfg.region) == 'undefined') {
38618 Roo.log("Failed to add Panel, region was not set");
38622 var region = cfg.region;
38628 xitems = cfg.items;
38633 if ( region == 'center') {
38634 Roo.log("Center: " + cfg.title);
38640 case 'Content': // ContentPanel (el, cfg)
38641 case 'Scroll': // ContentPanel (el, cfg)
38643 cfg.autoCreate = cfg.autoCreate || true;
38644 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38646 // var el = this.el.createChild();
38647 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38650 this.add(region, ret);
38654 case 'TreePanel': // our new panel!
38655 cfg.el = this.el.createChild();
38656 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38657 this.add(region, ret);
38662 // create a new Layout (which is a Border Layout...
38664 var clayout = cfg.layout;
38665 clayout.el = this.el.createChild();
38666 clayout.items = clayout.items || [];
38670 // replace this exitems with the clayout ones..
38671 xitems = clayout.items;
38673 // force background off if it's in center...
38674 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38675 cfg.background = false;
38677 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38680 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38681 //console.log('adding nested layout panel ' + cfg.toSource());
38682 this.add(region, ret);
38683 nb = {}; /// find first...
38688 // needs grid and region
38690 //var el = this.getRegion(region).el.createChild();
38692 *var el = this.el.createChild();
38693 // create the grid first...
38694 cfg.grid.container = el;
38695 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38698 if (region == 'center' && this.active ) {
38699 cfg.background = false;
38702 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38704 this.add(region, ret);
38706 if (cfg.background) {
38707 // render grid on panel activation (if panel background)
38708 ret.on('activate', function(gp) {
38709 if (!gp.grid.rendered) {
38710 // gp.grid.render(el);
38714 // cfg.grid.render(el);
38720 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38721 // it was the old xcomponent building that caused this before.
38722 // espeically if border is the top element in the tree.
38732 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38734 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38735 this.add(region, ret);
38739 throw "Can not add '" + cfg.xtype + "' to Border";
38745 this.beginUpdate();
38749 Roo.each(xitems, function(i) {
38750 region = nb && i.region ? i.region : false;
38752 var add = ret.addxtype(i);
38755 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38756 if (!i.background) {
38757 abn[region] = nb[region] ;
38764 // make the last non-background panel active..
38765 //if (nb) { Roo.log(abn); }
38768 for(var r in abn) {
38769 region = this.getRegion(r);
38771 // tried using nb[r], but it does not work..
38773 region.showPanel(abn[r]);
38784 factory : function(cfg)
38787 var validRegions = Roo.bootstrap.layout.Border.regions;
38789 var target = cfg.region;
38792 var r = Roo.bootstrap.layout;
38796 return new r.North(cfg);
38798 return new r.South(cfg);
38800 return new r.East(cfg);
38802 return new r.West(cfg);
38804 return new r.Center(cfg);
38806 throw 'Layout region "'+target+'" not supported.';
38813 * Ext JS Library 1.1.1
38814 * Copyright(c) 2006-2007, Ext JS, LLC.
38816 * Originally Released Under LGPL - original licence link has changed is not relivant.
38819 * <script type="text/javascript">
38823 * @class Roo.bootstrap.layout.Basic
38824 * @extends Roo.util.Observable
38825 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38826 * and does not have a titlebar, tabs or any other features. All it does is size and position
38827 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38828 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38829 * @cfg {string} region the region that it inhabits..
38830 * @cfg {bool} skipConfig skip config?
38834 Roo.bootstrap.layout.Basic = function(config){
38836 this.mgr = config.mgr;
38838 this.position = config.region;
38840 var skipConfig = config.skipConfig;
38844 * @scope Roo.BasicLayoutRegion
38848 * @event beforeremove
38849 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38850 * @param {Roo.LayoutRegion} this
38851 * @param {Roo.ContentPanel} panel The panel
38852 * @param {Object} e The cancel event object
38854 "beforeremove" : true,
38856 * @event invalidated
38857 * Fires when the layout for this region is changed.
38858 * @param {Roo.LayoutRegion} this
38860 "invalidated" : true,
38862 * @event visibilitychange
38863 * Fires when this region is shown or hidden
38864 * @param {Roo.LayoutRegion} this
38865 * @param {Boolean} visibility true or false
38867 "visibilitychange" : true,
38869 * @event paneladded
38870 * Fires when a panel is added.
38871 * @param {Roo.LayoutRegion} this
38872 * @param {Roo.ContentPanel} panel The panel
38874 "paneladded" : true,
38876 * @event panelremoved
38877 * Fires when a panel is removed.
38878 * @param {Roo.LayoutRegion} this
38879 * @param {Roo.ContentPanel} panel The panel
38881 "panelremoved" : true,
38883 * @event beforecollapse
38884 * Fires when this region before collapse.
38885 * @param {Roo.LayoutRegion} this
38887 "beforecollapse" : true,
38890 * Fires when this region is collapsed.
38891 * @param {Roo.LayoutRegion} this
38893 "collapsed" : true,
38896 * Fires when this region is expanded.
38897 * @param {Roo.LayoutRegion} this
38902 * Fires when this region is slid into view.
38903 * @param {Roo.LayoutRegion} this
38905 "slideshow" : true,
38908 * Fires when this region slides out of view.
38909 * @param {Roo.LayoutRegion} this
38911 "slidehide" : true,
38913 * @event panelactivated
38914 * Fires when a panel is activated.
38915 * @param {Roo.LayoutRegion} this
38916 * @param {Roo.ContentPanel} panel The activated panel
38918 "panelactivated" : true,
38921 * Fires when the user resizes this region.
38922 * @param {Roo.LayoutRegion} this
38923 * @param {Number} newSize The new size (width for east/west, height for north/south)
38927 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38928 this.panels = new Roo.util.MixedCollection();
38929 this.panels.getKey = this.getPanelId.createDelegate(this);
38931 this.activePanel = null;
38932 // ensure listeners are added...
38934 if (config.listeners || config.events) {
38935 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38936 listeners : config.listeners || {},
38937 events : config.events || {}
38941 if(skipConfig !== true){
38942 this.applyConfig(config);
38946 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38948 getPanelId : function(p){
38952 applyConfig : function(config){
38953 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38954 this.config = config;
38959 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38960 * the width, for horizontal (north, south) the height.
38961 * @param {Number} newSize The new width or height
38963 resizeTo : function(newSize){
38964 var el = this.el ? this.el :
38965 (this.activePanel ? this.activePanel.getEl() : null);
38967 switch(this.position){
38970 el.setWidth(newSize);
38971 this.fireEvent("resized", this, newSize);
38975 el.setHeight(newSize);
38976 this.fireEvent("resized", this, newSize);
38982 getBox : function(){
38983 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38986 getMargins : function(){
38987 return this.margins;
38990 updateBox : function(box){
38992 var el = this.activePanel.getEl();
38993 el.dom.style.left = box.x + "px";
38994 el.dom.style.top = box.y + "px";
38995 this.activePanel.setSize(box.width, box.height);
38999 * Returns the container element for this region.
39000 * @return {Roo.Element}
39002 getEl : function(){
39003 return this.activePanel;
39007 * Returns true if this region is currently visible.
39008 * @return {Boolean}
39010 isVisible : function(){
39011 return this.activePanel ? true : false;
39014 setActivePanel : function(panel){
39015 panel = this.getPanel(panel);
39016 if(this.activePanel && this.activePanel != panel){
39017 this.activePanel.setActiveState(false);
39018 this.activePanel.getEl().setLeftTop(-10000,-10000);
39020 this.activePanel = panel;
39021 panel.setActiveState(true);
39023 panel.setSize(this.box.width, this.box.height);
39025 this.fireEvent("panelactivated", this, panel);
39026 this.fireEvent("invalidated");
39030 * Show the specified panel.
39031 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39032 * @return {Roo.ContentPanel} The shown panel or null
39034 showPanel : function(panel){
39035 panel = this.getPanel(panel);
39037 this.setActivePanel(panel);
39043 * Get the active panel for this region.
39044 * @return {Roo.ContentPanel} The active panel or null
39046 getActivePanel : function(){
39047 return this.activePanel;
39051 * Add the passed ContentPanel(s)
39052 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39053 * @return {Roo.ContentPanel} The panel added (if only one was added)
39055 add : function(panel){
39056 if(arguments.length > 1){
39057 for(var i = 0, len = arguments.length; i < len; i++) {
39058 this.add(arguments[i]);
39062 if(this.hasPanel(panel)){
39063 this.showPanel(panel);
39066 var el = panel.getEl();
39067 if(el.dom.parentNode != this.mgr.el.dom){
39068 this.mgr.el.dom.appendChild(el.dom);
39070 if(panel.setRegion){
39071 panel.setRegion(this);
39073 this.panels.add(panel);
39074 el.setStyle("position", "absolute");
39075 if(!panel.background){
39076 this.setActivePanel(panel);
39077 if(this.config.initialSize && this.panels.getCount()==1){
39078 this.resizeTo(this.config.initialSize);
39081 this.fireEvent("paneladded", this, panel);
39086 * Returns true if the panel is in this region.
39087 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39088 * @return {Boolean}
39090 hasPanel : function(panel){
39091 if(typeof panel == "object"){ // must be panel obj
39092 panel = panel.getId();
39094 return this.getPanel(panel) ? true : false;
39098 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39099 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39100 * @param {Boolean} preservePanel Overrides the config preservePanel option
39101 * @return {Roo.ContentPanel} The panel that was removed
39103 remove : function(panel, preservePanel){
39104 panel = this.getPanel(panel);
39109 this.fireEvent("beforeremove", this, panel, e);
39110 if(e.cancel === true){
39113 var panelId = panel.getId();
39114 this.panels.removeKey(panelId);
39119 * Returns the panel specified or null if it's not in this region.
39120 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39121 * @return {Roo.ContentPanel}
39123 getPanel : function(id){
39124 if(typeof id == "object"){ // must be panel obj
39127 return this.panels.get(id);
39131 * Returns this regions position (north/south/east/west/center).
39134 getPosition: function(){
39135 return this.position;
39139 * Ext JS Library 1.1.1
39140 * Copyright(c) 2006-2007, Ext JS, LLC.
39142 * Originally Released Under LGPL - original licence link has changed is not relivant.
39145 * <script type="text/javascript">
39149 * @class Roo.bootstrap.layout.Region
39150 * @extends Roo.bootstrap.layout.Basic
39151 * This class represents a region in a layout manager.
39153 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39154 * @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})
39155 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39156 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39157 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39158 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39159 * @cfg {String} title The title for the region (overrides panel titles)
39160 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39161 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39162 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39163 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39164 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39165 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39166 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39167 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39168 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39169 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39171 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39172 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39173 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39174 * @cfg {Number} width For East/West panels
39175 * @cfg {Number} height For North/South panels
39176 * @cfg {Boolean} split To show the splitter
39177 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39179 * @cfg {string} cls Extra CSS classes to add to region
39181 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39182 * @cfg {string} region the region that it inhabits..
39185 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39186 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39188 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39189 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39190 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39192 Roo.bootstrap.layout.Region = function(config)
39194 this.applyConfig(config);
39196 var mgr = config.mgr;
39197 var pos = config.region;
39198 config.skipConfig = true;
39199 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39202 this.onRender(mgr.el);
39205 this.visible = true;
39206 this.collapsed = false;
39207 this.unrendered_panels = [];
39210 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39212 position: '', // set by wrapper (eg. north/south etc..)
39213 unrendered_panels : null, // unrendered panels.
39215 tabPosition : false,
39217 mgr: false, // points to 'Border'
39220 createBody : function(){
39221 /** This region's body element
39222 * @type Roo.Element */
39223 this.bodyEl = this.el.createChild({
39225 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39229 onRender: function(ctr, pos)
39231 var dh = Roo.DomHelper;
39232 /** This region's container element
39233 * @type Roo.Element */
39234 this.el = dh.append(ctr.dom, {
39236 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39238 /** This region's title element
39239 * @type Roo.Element */
39241 this.titleEl = dh.append(this.el.dom, {
39243 unselectable: "on",
39244 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39246 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39247 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39251 this.titleEl.enableDisplayMode();
39252 /** This region's title text element
39253 * @type HTMLElement */
39254 this.titleTextEl = this.titleEl.dom.firstChild;
39255 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39257 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39258 this.closeBtn.enableDisplayMode();
39259 this.closeBtn.on("click", this.closeClicked, this);
39260 this.closeBtn.hide();
39262 this.createBody(this.config);
39263 if(this.config.hideWhenEmpty){
39265 this.on("paneladded", this.validateVisibility, this);
39266 this.on("panelremoved", this.validateVisibility, this);
39268 if(this.autoScroll){
39269 this.bodyEl.setStyle("overflow", "auto");
39271 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39273 //if(c.titlebar !== false){
39274 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39275 this.titleEl.hide();
39277 this.titleEl.show();
39278 if(this.config.title){
39279 this.titleTextEl.innerHTML = this.config.title;
39283 if(this.config.collapsed){
39284 this.collapse(true);
39286 if(this.config.hidden){
39290 if (this.unrendered_panels && this.unrendered_panels.length) {
39291 for (var i =0;i< this.unrendered_panels.length; i++) {
39292 this.add(this.unrendered_panels[i]);
39294 this.unrendered_panels = null;
39300 applyConfig : function(c)
39303 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39304 var dh = Roo.DomHelper;
39305 if(c.titlebar !== false){
39306 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39307 this.collapseBtn.on("click", this.collapse, this);
39308 this.collapseBtn.enableDisplayMode();
39310 if(c.showPin === true || this.showPin){
39311 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39312 this.stickBtn.enableDisplayMode();
39313 this.stickBtn.on("click", this.expand, this);
39314 this.stickBtn.hide();
39319 /** This region's collapsed element
39320 * @type Roo.Element */
39323 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39324 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39327 if(c.floatable !== false){
39328 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39329 this.collapsedEl.on("click", this.collapseClick, this);
39332 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39333 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39334 id: "message", unselectable: "on", style:{"float":"left"}});
39335 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39337 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39338 this.expandBtn.on("click", this.expand, this);
39342 if(this.collapseBtn){
39343 this.collapseBtn.setVisible(c.collapsible == true);
39346 this.cmargins = c.cmargins || this.cmargins ||
39347 (this.position == "west" || this.position == "east" ?
39348 {top: 0, left: 2, right:2, bottom: 0} :
39349 {top: 2, left: 0, right:0, bottom: 2});
39351 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39354 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39356 this.autoScroll = c.autoScroll || false;
39361 this.duration = c.duration || .30;
39362 this.slideDuration = c.slideDuration || .45;
39367 * Returns true if this region is currently visible.
39368 * @return {Boolean}
39370 isVisible : function(){
39371 return this.visible;
39375 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39376 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39378 //setCollapsedTitle : function(title){
39379 // title = title || " ";
39380 // if(this.collapsedTitleTextEl){
39381 // this.collapsedTitleTextEl.innerHTML = title;
39385 getBox : function(){
39387 // if(!this.collapsed){
39388 b = this.el.getBox(false, true);
39390 // b = this.collapsedEl.getBox(false, true);
39395 getMargins : function(){
39396 return this.margins;
39397 //return this.collapsed ? this.cmargins : this.margins;
39400 highlight : function(){
39401 this.el.addClass("x-layout-panel-dragover");
39404 unhighlight : function(){
39405 this.el.removeClass("x-layout-panel-dragover");
39408 updateBox : function(box)
39410 if (!this.bodyEl) {
39411 return; // not rendered yet..
39415 if(!this.collapsed){
39416 this.el.dom.style.left = box.x + "px";
39417 this.el.dom.style.top = box.y + "px";
39418 this.updateBody(box.width, box.height);
39420 this.collapsedEl.dom.style.left = box.x + "px";
39421 this.collapsedEl.dom.style.top = box.y + "px";
39422 this.collapsedEl.setSize(box.width, box.height);
39425 this.tabs.autoSizeTabs();
39429 updateBody : function(w, h)
39432 this.el.setWidth(w);
39433 w -= this.el.getBorderWidth("rl");
39434 if(this.config.adjustments){
39435 w += this.config.adjustments[0];
39438 if(h !== null && h > 0){
39439 this.el.setHeight(h);
39440 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39441 h -= this.el.getBorderWidth("tb");
39442 if(this.config.adjustments){
39443 h += this.config.adjustments[1];
39445 this.bodyEl.setHeight(h);
39447 h = this.tabs.syncHeight(h);
39450 if(this.panelSize){
39451 w = w !== null ? w : this.panelSize.width;
39452 h = h !== null ? h : this.panelSize.height;
39454 if(this.activePanel){
39455 var el = this.activePanel.getEl();
39456 w = w !== null ? w : el.getWidth();
39457 h = h !== null ? h : el.getHeight();
39458 this.panelSize = {width: w, height: h};
39459 this.activePanel.setSize(w, h);
39461 if(Roo.isIE && this.tabs){
39462 this.tabs.el.repaint();
39467 * Returns the container element for this region.
39468 * @return {Roo.Element}
39470 getEl : function(){
39475 * Hides this region.
39478 //if(!this.collapsed){
39479 this.el.dom.style.left = "-2000px";
39482 // this.collapsedEl.dom.style.left = "-2000px";
39483 // this.collapsedEl.hide();
39485 this.visible = false;
39486 this.fireEvent("visibilitychange", this, false);
39490 * Shows this region if it was previously hidden.
39493 //if(!this.collapsed){
39496 // this.collapsedEl.show();
39498 this.visible = true;
39499 this.fireEvent("visibilitychange", this, true);
39502 closeClicked : function(){
39503 if(this.activePanel){
39504 this.remove(this.activePanel);
39508 collapseClick : function(e){
39510 e.stopPropagation();
39513 e.stopPropagation();
39519 * Collapses this region.
39520 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39523 collapse : function(skipAnim, skipCheck = false){
39524 if(this.collapsed) {
39528 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39530 this.collapsed = true;
39532 this.split.el.hide();
39534 if(this.config.animate && skipAnim !== true){
39535 this.fireEvent("invalidated", this);
39536 this.animateCollapse();
39538 this.el.setLocation(-20000,-20000);
39540 this.collapsedEl.show();
39541 this.fireEvent("collapsed", this);
39542 this.fireEvent("invalidated", this);
39548 animateCollapse : function(){
39553 * Expands this region if it was previously collapsed.
39554 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39555 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39558 expand : function(e, skipAnim){
39560 e.stopPropagation();
39562 if(!this.collapsed || this.el.hasActiveFx()) {
39566 this.afterSlideIn();
39569 this.collapsed = false;
39570 if(this.config.animate && skipAnim !== true){
39571 this.animateExpand();
39575 this.split.el.show();
39577 this.collapsedEl.setLocation(-2000,-2000);
39578 this.collapsedEl.hide();
39579 this.fireEvent("invalidated", this);
39580 this.fireEvent("expanded", this);
39584 animateExpand : function(){
39588 initTabs : function()
39590 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39592 var ts = new Roo.bootstrap.panel.Tabs({
39593 el: this.bodyEl.dom,
39595 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39596 disableTooltips: this.config.disableTabTips,
39597 toolbar : this.config.toolbar
39600 if(this.config.hideTabs){
39601 ts.stripWrap.setDisplayed(false);
39604 ts.resizeTabs = this.config.resizeTabs === true;
39605 ts.minTabWidth = this.config.minTabWidth || 40;
39606 ts.maxTabWidth = this.config.maxTabWidth || 250;
39607 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39608 ts.monitorResize = false;
39609 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39610 ts.bodyEl.addClass('roo-layout-tabs-body');
39611 this.panels.each(this.initPanelAsTab, this);
39614 initPanelAsTab : function(panel){
39615 var ti = this.tabs.addTab(
39619 this.config.closeOnTab && panel.isClosable(),
39622 if(panel.tabTip !== undefined){
39623 ti.setTooltip(panel.tabTip);
39625 ti.on("activate", function(){
39626 this.setActivePanel(panel);
39629 if(this.config.closeOnTab){
39630 ti.on("beforeclose", function(t, e){
39632 this.remove(panel);
39636 panel.tabItem = ti;
39641 updatePanelTitle : function(panel, title)
39643 if(this.activePanel == panel){
39644 this.updateTitle(title);
39647 var ti = this.tabs.getTab(panel.getEl().id);
39649 if(panel.tabTip !== undefined){
39650 ti.setTooltip(panel.tabTip);
39655 updateTitle : function(title){
39656 if(this.titleTextEl && !this.config.title){
39657 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39661 setActivePanel : function(panel)
39663 panel = this.getPanel(panel);
39664 if(this.activePanel && this.activePanel != panel){
39665 if(this.activePanel.setActiveState(false) === false){
39669 this.activePanel = panel;
39670 panel.setActiveState(true);
39671 if(this.panelSize){
39672 panel.setSize(this.panelSize.width, this.panelSize.height);
39675 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39677 this.updateTitle(panel.getTitle());
39679 this.fireEvent("invalidated", this);
39681 this.fireEvent("panelactivated", this, panel);
39685 * Shows the specified panel.
39686 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39687 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39689 showPanel : function(panel)
39691 panel = this.getPanel(panel);
39694 var tab = this.tabs.getTab(panel.getEl().id);
39695 if(tab.isHidden()){
39696 this.tabs.unhideTab(tab.id);
39700 this.setActivePanel(panel);
39707 * Get the active panel for this region.
39708 * @return {Roo.ContentPanel} The active panel or null
39710 getActivePanel : function(){
39711 return this.activePanel;
39714 validateVisibility : function(){
39715 if(this.panels.getCount() < 1){
39716 this.updateTitle(" ");
39717 this.closeBtn.hide();
39720 if(!this.isVisible()){
39727 * Adds the passed ContentPanel(s) to this region.
39728 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39729 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39731 add : function(panel)
39733 if(arguments.length > 1){
39734 for(var i = 0, len = arguments.length; i < len; i++) {
39735 this.add(arguments[i]);
39740 // if we have not been rendered yet, then we can not really do much of this..
39741 if (!this.bodyEl) {
39742 this.unrendered_panels.push(panel);
39749 if(this.hasPanel(panel)){
39750 this.showPanel(panel);
39753 panel.setRegion(this);
39754 this.panels.add(panel);
39755 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39756 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39757 // and hide them... ???
39758 this.bodyEl.dom.appendChild(panel.getEl().dom);
39759 if(panel.background !== true){
39760 this.setActivePanel(panel);
39762 this.fireEvent("paneladded", this, panel);
39769 this.initPanelAsTab(panel);
39773 if(panel.background !== true){
39774 this.tabs.activate(panel.getEl().id);
39776 this.fireEvent("paneladded", this, panel);
39781 * Hides the tab for the specified panel.
39782 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39784 hidePanel : function(panel){
39785 if(this.tabs && (panel = this.getPanel(panel))){
39786 this.tabs.hideTab(panel.getEl().id);
39791 * Unhides the tab for a previously hidden panel.
39792 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39794 unhidePanel : function(panel){
39795 if(this.tabs && (panel = this.getPanel(panel))){
39796 this.tabs.unhideTab(panel.getEl().id);
39800 clearPanels : function(){
39801 while(this.panels.getCount() > 0){
39802 this.remove(this.panels.first());
39807 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39808 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39809 * @param {Boolean} preservePanel Overrides the config preservePanel option
39810 * @return {Roo.ContentPanel} The panel that was removed
39812 remove : function(panel, preservePanel)
39814 panel = this.getPanel(panel);
39819 this.fireEvent("beforeremove", this, panel, e);
39820 if(e.cancel === true){
39823 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39824 var panelId = panel.getId();
39825 this.panels.removeKey(panelId);
39827 document.body.appendChild(panel.getEl().dom);
39830 this.tabs.removeTab(panel.getEl().id);
39831 }else if (!preservePanel){
39832 this.bodyEl.dom.removeChild(panel.getEl().dom);
39834 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39835 var p = this.panels.first();
39836 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39837 tempEl.appendChild(p.getEl().dom);
39838 this.bodyEl.update("");
39839 this.bodyEl.dom.appendChild(p.getEl().dom);
39841 this.updateTitle(p.getTitle());
39843 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39844 this.setActivePanel(p);
39846 panel.setRegion(null);
39847 if(this.activePanel == panel){
39848 this.activePanel = null;
39850 if(this.config.autoDestroy !== false && preservePanel !== true){
39851 try{panel.destroy();}catch(e){}
39853 this.fireEvent("panelremoved", this, panel);
39858 * Returns the TabPanel component used by this region
39859 * @return {Roo.TabPanel}
39861 getTabs : function(){
39865 createTool : function(parentEl, className){
39866 var btn = Roo.DomHelper.append(parentEl, {
39868 cls: "x-layout-tools-button",
39871 cls: "roo-layout-tools-button-inner " + className,
39875 btn.addClassOnOver("roo-layout-tools-button-over");
39880 * Ext JS Library 1.1.1
39881 * Copyright(c) 2006-2007, Ext JS, LLC.
39883 * Originally Released Under LGPL - original licence link has changed is not relivant.
39886 * <script type="text/javascript">
39892 * @class Roo.SplitLayoutRegion
39893 * @extends Roo.LayoutRegion
39894 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39896 Roo.bootstrap.layout.Split = function(config){
39897 this.cursor = config.cursor;
39898 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39901 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39903 splitTip : "Drag to resize.",
39904 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39905 useSplitTips : false,
39907 applyConfig : function(config){
39908 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39911 onRender : function(ctr,pos) {
39913 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39914 if(!this.config.split){
39919 var splitEl = Roo.DomHelper.append(ctr.dom, {
39921 id: this.el.id + "-split",
39922 cls: "roo-layout-split roo-layout-split-"+this.position,
39925 /** The SplitBar for this region
39926 * @type Roo.SplitBar */
39927 // does not exist yet...
39928 Roo.log([this.position, this.orientation]);
39930 this.split = new Roo.bootstrap.SplitBar({
39931 dragElement : splitEl,
39932 resizingElement: this.el,
39933 orientation : this.orientation
39936 this.split.on("moved", this.onSplitMove, this);
39937 this.split.useShim = this.config.useShim === true;
39938 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39939 if(this.useSplitTips){
39940 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39942 //if(config.collapsible){
39943 // this.split.el.on("dblclick", this.collapse, this);
39946 if(typeof this.config.minSize != "undefined"){
39947 this.split.minSize = this.config.minSize;
39949 if(typeof this.config.maxSize != "undefined"){
39950 this.split.maxSize = this.config.maxSize;
39952 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39953 this.hideSplitter();
39958 getHMaxSize : function(){
39959 var cmax = this.config.maxSize || 10000;
39960 var center = this.mgr.getRegion("center");
39961 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39964 getVMaxSize : function(){
39965 var cmax = this.config.maxSize || 10000;
39966 var center = this.mgr.getRegion("center");
39967 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39970 onSplitMove : function(split, newSize){
39971 this.fireEvent("resized", this, newSize);
39975 * Returns the {@link Roo.SplitBar} for this region.
39976 * @return {Roo.SplitBar}
39978 getSplitBar : function(){
39983 this.hideSplitter();
39984 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39987 hideSplitter : function(){
39989 this.split.el.setLocation(-2000,-2000);
39990 this.split.el.hide();
39996 this.split.el.show();
39998 Roo.bootstrap.layout.Split.superclass.show.call(this);
40001 beforeSlide: function(){
40002 if(Roo.isGecko){// firefox overflow auto bug workaround
40003 this.bodyEl.clip();
40005 this.tabs.bodyEl.clip();
40007 if(this.activePanel){
40008 this.activePanel.getEl().clip();
40010 if(this.activePanel.beforeSlide){
40011 this.activePanel.beforeSlide();
40017 afterSlide : function(){
40018 if(Roo.isGecko){// firefox overflow auto bug workaround
40019 this.bodyEl.unclip();
40021 this.tabs.bodyEl.unclip();
40023 if(this.activePanel){
40024 this.activePanel.getEl().unclip();
40025 if(this.activePanel.afterSlide){
40026 this.activePanel.afterSlide();
40032 initAutoHide : function(){
40033 if(this.autoHide !== false){
40034 if(!this.autoHideHd){
40035 var st = new Roo.util.DelayedTask(this.slideIn, this);
40036 this.autoHideHd = {
40037 "mouseout": function(e){
40038 if(!e.within(this.el, true)){
40042 "mouseover" : function(e){
40048 this.el.on(this.autoHideHd);
40052 clearAutoHide : function(){
40053 if(this.autoHide !== false){
40054 this.el.un("mouseout", this.autoHideHd.mouseout);
40055 this.el.un("mouseover", this.autoHideHd.mouseover);
40059 clearMonitor : function(){
40060 Roo.get(document).un("click", this.slideInIf, this);
40063 // these names are backwards but not changed for compat
40064 slideOut : function(){
40065 if(this.isSlid || this.el.hasActiveFx()){
40068 this.isSlid = true;
40069 if(this.collapseBtn){
40070 this.collapseBtn.hide();
40072 this.closeBtnState = this.closeBtn.getStyle('display');
40073 this.closeBtn.hide();
40075 this.stickBtn.show();
40078 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40079 this.beforeSlide();
40080 this.el.setStyle("z-index", 10001);
40081 this.el.slideIn(this.getSlideAnchor(), {
40082 callback: function(){
40084 this.initAutoHide();
40085 Roo.get(document).on("click", this.slideInIf, this);
40086 this.fireEvent("slideshow", this);
40093 afterSlideIn : function(){
40094 this.clearAutoHide();
40095 this.isSlid = false;
40096 this.clearMonitor();
40097 this.el.setStyle("z-index", "");
40098 if(this.collapseBtn){
40099 this.collapseBtn.show();
40101 this.closeBtn.setStyle('display', this.closeBtnState);
40103 this.stickBtn.hide();
40105 this.fireEvent("slidehide", this);
40108 slideIn : function(cb){
40109 if(!this.isSlid || this.el.hasActiveFx()){
40113 this.isSlid = false;
40114 this.beforeSlide();
40115 this.el.slideOut(this.getSlideAnchor(), {
40116 callback: function(){
40117 this.el.setLeftTop(-10000, -10000);
40119 this.afterSlideIn();
40127 slideInIf : function(e){
40128 if(!e.within(this.el)){
40133 animateCollapse : function(){
40134 this.beforeSlide();
40135 this.el.setStyle("z-index", 20000);
40136 var anchor = this.getSlideAnchor();
40137 this.el.slideOut(anchor, {
40138 callback : function(){
40139 this.el.setStyle("z-index", "");
40140 this.collapsedEl.slideIn(anchor, {duration:.3});
40142 this.el.setLocation(-10000,-10000);
40144 this.fireEvent("collapsed", this);
40151 animateExpand : function(){
40152 this.beforeSlide();
40153 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40154 this.el.setStyle("z-index", 20000);
40155 this.collapsedEl.hide({
40158 this.el.slideIn(this.getSlideAnchor(), {
40159 callback : function(){
40160 this.el.setStyle("z-index", "");
40163 this.split.el.show();
40165 this.fireEvent("invalidated", this);
40166 this.fireEvent("expanded", this);
40194 getAnchor : function(){
40195 return this.anchors[this.position];
40198 getCollapseAnchor : function(){
40199 return this.canchors[this.position];
40202 getSlideAnchor : function(){
40203 return this.sanchors[this.position];
40206 getAlignAdj : function(){
40207 var cm = this.cmargins;
40208 switch(this.position){
40224 getExpandAdj : function(){
40225 var c = this.collapsedEl, cm = this.cmargins;
40226 switch(this.position){
40228 return [-(cm.right+c.getWidth()+cm.left), 0];
40231 return [cm.right+c.getWidth()+cm.left, 0];
40234 return [0, -(cm.top+cm.bottom+c.getHeight())];
40237 return [0, cm.top+cm.bottom+c.getHeight()];
40243 * Ext JS Library 1.1.1
40244 * Copyright(c) 2006-2007, Ext JS, LLC.
40246 * Originally Released Under LGPL - original licence link has changed is not relivant.
40249 * <script type="text/javascript">
40252 * These classes are private internal classes
40254 Roo.bootstrap.layout.Center = function(config){
40255 config.region = "center";
40256 Roo.bootstrap.layout.Region.call(this, config);
40257 this.visible = true;
40258 this.minWidth = config.minWidth || 20;
40259 this.minHeight = config.minHeight || 20;
40262 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40264 // center panel can't be hidden
40268 // center panel can't be hidden
40271 getMinWidth: function(){
40272 return this.minWidth;
40275 getMinHeight: function(){
40276 return this.minHeight;
40290 Roo.bootstrap.layout.North = function(config)
40292 config.region = 'north';
40293 config.cursor = 'n-resize';
40295 Roo.bootstrap.layout.Split.call(this, config);
40299 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40300 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40301 this.split.el.addClass("roo-layout-split-v");
40303 //var size = config.initialSize || config.height;
40304 //if(this.el && typeof size != "undefined"){
40305 // this.el.setHeight(size);
40308 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40310 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40313 onRender : function(ctr, pos)
40315 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40316 var size = this.config.initialSize || this.config.height;
40317 if(this.el && typeof size != "undefined"){
40318 this.el.setHeight(size);
40323 getBox : function(){
40324 if(this.collapsed){
40325 return this.collapsedEl.getBox();
40327 var box = this.el.getBox();
40329 box.height += this.split.el.getHeight();
40334 updateBox : function(box){
40335 if(this.split && !this.collapsed){
40336 box.height -= this.split.el.getHeight();
40337 this.split.el.setLeft(box.x);
40338 this.split.el.setTop(box.y+box.height);
40339 this.split.el.setWidth(box.width);
40341 if(this.collapsed){
40342 this.updateBody(box.width, null);
40344 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40352 Roo.bootstrap.layout.South = function(config){
40353 config.region = 'south';
40354 config.cursor = 's-resize';
40355 Roo.bootstrap.layout.Split.call(this, config);
40357 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40358 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40359 this.split.el.addClass("roo-layout-split-v");
40364 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40365 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40367 onRender : function(ctr, pos)
40369 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40370 var size = this.config.initialSize || this.config.height;
40371 if(this.el && typeof size != "undefined"){
40372 this.el.setHeight(size);
40377 getBox : function(){
40378 if(this.collapsed){
40379 return this.collapsedEl.getBox();
40381 var box = this.el.getBox();
40383 var sh = this.split.el.getHeight();
40390 updateBox : function(box){
40391 if(this.split && !this.collapsed){
40392 var sh = this.split.el.getHeight();
40395 this.split.el.setLeft(box.x);
40396 this.split.el.setTop(box.y-sh);
40397 this.split.el.setWidth(box.width);
40399 if(this.collapsed){
40400 this.updateBody(box.width, null);
40402 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40406 Roo.bootstrap.layout.East = function(config){
40407 config.region = "east";
40408 config.cursor = "e-resize";
40409 Roo.bootstrap.layout.Split.call(this, config);
40411 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40412 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40413 this.split.el.addClass("roo-layout-split-h");
40417 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40418 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40420 onRender : function(ctr, pos)
40422 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40423 var size = this.config.initialSize || this.config.width;
40424 if(this.el && typeof size != "undefined"){
40425 this.el.setWidth(size);
40430 getBox : function(){
40431 if(this.collapsed){
40432 return this.collapsedEl.getBox();
40434 var box = this.el.getBox();
40436 var sw = this.split.el.getWidth();
40443 updateBox : function(box){
40444 if(this.split && !this.collapsed){
40445 var sw = this.split.el.getWidth();
40447 this.split.el.setLeft(box.x);
40448 this.split.el.setTop(box.y);
40449 this.split.el.setHeight(box.height);
40452 if(this.collapsed){
40453 this.updateBody(null, box.height);
40455 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40459 Roo.bootstrap.layout.West = function(config){
40460 config.region = "west";
40461 config.cursor = "w-resize";
40463 Roo.bootstrap.layout.Split.call(this, config);
40465 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40466 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40467 this.split.el.addClass("roo-layout-split-h");
40471 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40472 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40474 onRender: function(ctr, pos)
40476 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40477 var size = this.config.initialSize || this.config.width;
40478 if(typeof size != "undefined"){
40479 this.el.setWidth(size);
40483 getBox : function(){
40484 if(this.collapsed){
40485 return this.collapsedEl.getBox();
40487 var box = this.el.getBox();
40488 if (box.width == 0) {
40489 box.width = this.config.width; // kludge?
40492 box.width += this.split.el.getWidth();
40497 updateBox : function(box){
40498 if(this.split && !this.collapsed){
40499 var sw = this.split.el.getWidth();
40501 this.split.el.setLeft(box.x+box.width);
40502 this.split.el.setTop(box.y);
40503 this.split.el.setHeight(box.height);
40505 if(this.collapsed){
40506 this.updateBody(null, box.height);
40508 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40510 });Roo.namespace("Roo.bootstrap.panel");/*
40512 * Ext JS Library 1.1.1
40513 * Copyright(c) 2006-2007, Ext JS, LLC.
40515 * Originally Released Under LGPL - original licence link has changed is not relivant.
40518 * <script type="text/javascript">
40521 * @class Roo.ContentPanel
40522 * @extends Roo.util.Observable
40523 * A basic ContentPanel element.
40524 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40525 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40526 * @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
40527 * @cfg {Boolean} closable True if the panel can be closed/removed
40528 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40529 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40530 * @cfg {Toolbar} toolbar A toolbar for this panel
40531 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40532 * @cfg {String} title The title for this panel
40533 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40534 * @cfg {String} url Calls {@link #setUrl} with this value
40535 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40536 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40537 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40538 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40539 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40540 * @cfg {Boolean} badges render the badges
40541 * @cfg {String} cls extra classes to use
40542 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40545 * Create a new ContentPanel.
40546 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40547 * @param {String/Object} config A string to set only the title or a config object
40548 * @param {String} content (optional) Set the HTML content for this panel
40549 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40551 Roo.bootstrap.panel.Content = function( config){
40553 this.tpl = config.tpl || false;
40555 var el = config.el;
40556 var content = config.content;
40558 if(config.autoCreate){ // xtype is available if this is called from factory
40561 this.el = Roo.get(el);
40562 if(!this.el && config && config.autoCreate){
40563 if(typeof config.autoCreate == "object"){
40564 if(!config.autoCreate.id){
40565 config.autoCreate.id = config.id||el;
40567 this.el = Roo.DomHelper.append(document.body,
40568 config.autoCreate, true);
40572 cls: (config.cls || '') +
40573 (config.background ? ' bg-' + config.background : '') +
40574 " roo-layout-inactive-content",
40577 if (config.iframe) {
40581 style : 'border: 0px',
40582 src : 'about:blank'
40588 elcfg.html = config.html;
40592 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40593 if (config.iframe) {
40594 this.iframeEl = this.el.select('iframe',true).first();
40599 this.closable = false;
40600 this.loaded = false;
40601 this.active = false;
40604 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40606 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40608 this.wrapEl = this.el; //this.el.wrap();
40610 if (config.toolbar.items) {
40611 ti = config.toolbar.items ;
40612 delete config.toolbar.items ;
40616 this.toolbar.render(this.wrapEl, 'before');
40617 for(var i =0;i < ti.length;i++) {
40618 // Roo.log(['add child', items[i]]);
40619 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40621 this.toolbar.items = nitems;
40622 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40623 delete config.toolbar;
40627 // xtype created footer. - not sure if will work as we normally have to render first..
40628 if (this.footer && !this.footer.el && this.footer.xtype) {
40629 if (!this.wrapEl) {
40630 this.wrapEl = this.el.wrap();
40633 this.footer.container = this.wrapEl.createChild();
40635 this.footer = Roo.factory(this.footer, Roo);
40640 if(typeof config == "string"){
40641 this.title = config;
40643 Roo.apply(this, config);
40647 this.resizeEl = Roo.get(this.resizeEl, true);
40649 this.resizeEl = this.el;
40651 // handle view.xtype
40659 * Fires when this panel is activated.
40660 * @param {Roo.ContentPanel} this
40664 * @event deactivate
40665 * Fires when this panel is activated.
40666 * @param {Roo.ContentPanel} this
40668 "deactivate" : true,
40672 * Fires when this panel is resized if fitToFrame is true.
40673 * @param {Roo.ContentPanel} this
40674 * @param {Number} width The width after any component adjustments
40675 * @param {Number} height The height after any component adjustments
40681 * Fires when this tab is created
40682 * @param {Roo.ContentPanel} this
40688 * Fires when this content is scrolled
40689 * @param {Roo.ContentPanel} this
40690 * @param {Event} scrollEvent
40701 if(this.autoScroll && !this.iframe){
40702 this.resizeEl.setStyle("overflow", "auto");
40703 this.resizeEl.on('scroll', this.onScroll, this);
40705 // fix randome scrolling
40706 //this.el.on('scroll', function() {
40707 // Roo.log('fix random scolling');
40708 // this.scrollTo('top',0);
40711 content = content || this.content;
40713 this.setContent(content);
40715 if(config && config.url){
40716 this.setUrl(this.url, this.params, this.loadOnce);
40721 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40723 if (this.view && typeof(this.view.xtype) != 'undefined') {
40724 this.view.el = this.el.appendChild(document.createElement("div"));
40725 this.view = Roo.factory(this.view);
40726 this.view.render && this.view.render(false, '');
40730 this.fireEvent('render', this);
40733 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40743 /* Resize Element - use this to work out scroll etc. */
40746 setRegion : function(region){
40747 this.region = region;
40748 this.setActiveClass(region && !this.background);
40752 setActiveClass: function(state)
40755 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40756 this.el.setStyle('position','relative');
40758 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40759 this.el.setStyle('position', 'absolute');
40764 * Returns the toolbar for this Panel if one was configured.
40765 * @return {Roo.Toolbar}
40767 getToolbar : function(){
40768 return this.toolbar;
40771 setActiveState : function(active)
40773 this.active = active;
40774 this.setActiveClass(active);
40776 if(this.fireEvent("deactivate", this) === false){
40781 this.fireEvent("activate", this);
40785 * Updates this panel's element (not for iframe)
40786 * @param {String} content The new content
40787 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40789 setContent : function(content, loadScripts){
40794 this.el.update(content, loadScripts);
40797 ignoreResize : function(w, h){
40798 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40801 this.lastSize = {width: w, height: h};
40806 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40807 * @return {Roo.UpdateManager} The UpdateManager
40809 getUpdateManager : function(){
40813 return this.el.getUpdateManager();
40816 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40817 * Does not work with IFRAME contents
40818 * @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:
40821 url: "your-url.php",
40822 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40823 callback: yourFunction,
40824 scope: yourObject, //(optional scope)
40827 text: "Loading...",
40833 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40834 * 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.
40835 * @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}
40836 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40837 * @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.
40838 * @return {Roo.ContentPanel} this
40846 var um = this.el.getUpdateManager();
40847 um.update.apply(um, arguments);
40853 * 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.
40854 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40855 * @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)
40856 * @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)
40857 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40859 setUrl : function(url, params, loadOnce){
40861 this.iframeEl.dom.src = url;
40865 if(this.refreshDelegate){
40866 this.removeListener("activate", this.refreshDelegate);
40868 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40869 this.on("activate", this.refreshDelegate);
40870 return this.el.getUpdateManager();
40873 _handleRefresh : function(url, params, loadOnce){
40874 if(!loadOnce || !this.loaded){
40875 var updater = this.el.getUpdateManager();
40876 updater.update(url, params, this._setLoaded.createDelegate(this));
40880 _setLoaded : function(){
40881 this.loaded = true;
40885 * Returns this panel's id
40888 getId : function(){
40893 * Returns this panel's element - used by regiosn to add.
40894 * @return {Roo.Element}
40896 getEl : function(){
40897 return this.wrapEl || this.el;
40902 adjustForComponents : function(width, height)
40904 //Roo.log('adjustForComponents ');
40905 if(this.resizeEl != this.el){
40906 width -= this.el.getFrameWidth('lr');
40907 height -= this.el.getFrameWidth('tb');
40910 var te = this.toolbar.getEl();
40911 te.setWidth(width);
40912 height -= te.getHeight();
40915 var te = this.footer.getEl();
40916 te.setWidth(width);
40917 height -= te.getHeight();
40921 if(this.adjustments){
40922 width += this.adjustments[0];
40923 height += this.adjustments[1];
40925 return {"width": width, "height": height};
40928 setSize : function(width, height){
40929 if(this.fitToFrame && !this.ignoreResize(width, height)){
40930 if(this.fitContainer && this.resizeEl != this.el){
40931 this.el.setSize(width, height);
40933 var size = this.adjustForComponents(width, height);
40935 this.iframeEl.setSize(width,height);
40938 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40939 this.fireEvent('resize', this, size.width, size.height);
40946 * Returns this panel's title
40949 getTitle : function(){
40951 if (typeof(this.title) != 'object') {
40956 for (var k in this.title) {
40957 if (!this.title.hasOwnProperty(k)) {
40961 if (k.indexOf('-') >= 0) {
40962 var s = k.split('-');
40963 for (var i = 0; i<s.length; i++) {
40964 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40967 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40974 * Set this panel's title
40975 * @param {String} title
40977 setTitle : function(title){
40978 this.title = title;
40980 this.region.updatePanelTitle(this, title);
40985 * Returns true is this panel was configured to be closable
40986 * @return {Boolean}
40988 isClosable : function(){
40989 return this.closable;
40992 beforeSlide : function(){
40994 this.resizeEl.clip();
40997 afterSlide : function(){
40999 this.resizeEl.unclip();
41003 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41004 * Will fail silently if the {@link #setUrl} method has not been called.
41005 * This does not activate the panel, just updates its content.
41007 refresh : function(){
41008 if(this.refreshDelegate){
41009 this.loaded = false;
41010 this.refreshDelegate();
41015 * Destroys this panel
41017 destroy : function(){
41018 this.el.removeAllListeners();
41019 var tempEl = document.createElement("span");
41020 tempEl.appendChild(this.el.dom);
41021 tempEl.innerHTML = "";
41027 * form - if the content panel contains a form - this is a reference to it.
41028 * @type {Roo.form.Form}
41032 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41033 * This contains a reference to it.
41039 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41049 * @param {Object} cfg Xtype definition of item to add.
41053 getChildContainer: function () {
41054 return this.getEl();
41058 onScroll : function(e)
41060 this.fireEvent('scroll', this, e);
41065 var ret = new Roo.factory(cfg);
41070 if (cfg.xtype.match(/^Form$/)) {
41073 //if (this.footer) {
41074 // el = this.footer.container.insertSibling(false, 'before');
41076 el = this.el.createChild();
41079 this.form = new Roo.form.Form(cfg);
41082 if ( this.form.allItems.length) {
41083 this.form.render(el.dom);
41087 // should only have one of theses..
41088 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41089 // views.. should not be just added - used named prop 'view''
41091 cfg.el = this.el.appendChild(document.createElement("div"));
41094 var ret = new Roo.factory(cfg);
41096 ret.render && ret.render(false, ''); // render blank..
41106 * @class Roo.bootstrap.panel.Grid
41107 * @extends Roo.bootstrap.panel.Content
41109 * Create a new GridPanel.
41110 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41111 * @param {Object} config A the config object
41117 Roo.bootstrap.panel.Grid = function(config)
41121 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41122 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41124 config.el = this.wrapper;
41125 //this.el = this.wrapper;
41127 if (config.container) {
41128 // ctor'ed from a Border/panel.grid
41131 this.wrapper.setStyle("overflow", "hidden");
41132 this.wrapper.addClass('roo-grid-container');
41137 if(config.toolbar){
41138 var tool_el = this.wrapper.createChild();
41139 this.toolbar = Roo.factory(config.toolbar);
41141 if (config.toolbar.items) {
41142 ti = config.toolbar.items ;
41143 delete config.toolbar.items ;
41147 this.toolbar.render(tool_el);
41148 for(var i =0;i < ti.length;i++) {
41149 // Roo.log(['add child', items[i]]);
41150 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41152 this.toolbar.items = nitems;
41154 delete config.toolbar;
41157 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41158 config.grid.scrollBody = true;;
41159 config.grid.monitorWindowResize = false; // turn off autosizing
41160 config.grid.autoHeight = false;
41161 config.grid.autoWidth = false;
41163 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41165 if (config.background) {
41166 // render grid on panel activation (if panel background)
41167 this.on('activate', function(gp) {
41168 if (!gp.grid.rendered) {
41169 gp.grid.render(this.wrapper);
41170 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41175 this.grid.render(this.wrapper);
41176 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41179 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41180 // ??? needed ??? config.el = this.wrapper;
41185 // xtype created footer. - not sure if will work as we normally have to render first..
41186 if (this.footer && !this.footer.el && this.footer.xtype) {
41188 var ctr = this.grid.getView().getFooterPanel(true);
41189 this.footer.dataSource = this.grid.dataSource;
41190 this.footer = Roo.factory(this.footer, Roo);
41191 this.footer.render(ctr);
41201 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41202 getId : function(){
41203 return this.grid.id;
41207 * Returns the grid for this panel
41208 * @return {Roo.bootstrap.Table}
41210 getGrid : function(){
41214 setSize : function(width, height){
41215 if(!this.ignoreResize(width, height)){
41216 var grid = this.grid;
41217 var size = this.adjustForComponents(width, height);
41218 // tfoot is not a footer?
41221 var gridel = grid.getGridEl();
41222 gridel.setSize(size.width, size.height);
41224 var tbd = grid.getGridEl().select('tbody', true).first();
41225 var thd = grid.getGridEl().select('thead',true).first();
41226 var tbf= grid.getGridEl().select('tfoot', true).first();
41229 size.height -= tbf.getHeight();
41232 size.height -= thd.getHeight();
41235 tbd.setSize(size.width, size.height );
41236 // this is for the account management tab -seems to work there.
41237 var thd = grid.getGridEl().select('thead',true).first();
41239 // tbd.setSize(size.width, size.height - thd.getHeight());
41248 beforeSlide : function(){
41249 this.grid.getView().scroller.clip();
41252 afterSlide : function(){
41253 this.grid.getView().scroller.unclip();
41256 destroy : function(){
41257 this.grid.destroy();
41259 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41264 * @class Roo.bootstrap.panel.Nest
41265 * @extends Roo.bootstrap.panel.Content
41267 * Create a new Panel, that can contain a layout.Border.
41270 * @param {Roo.BorderLayout} layout The layout for this panel
41271 * @param {String/Object} config A string to set only the title or a config object
41273 Roo.bootstrap.panel.Nest = function(config)
41275 // construct with only one argument..
41276 /* FIXME - implement nicer consturctors
41277 if (layout.layout) {
41279 layout = config.layout;
41280 delete config.layout;
41282 if (layout.xtype && !layout.getEl) {
41283 // then layout needs constructing..
41284 layout = Roo.factory(layout, Roo);
41288 config.el = config.layout.getEl();
41290 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41292 config.layout.monitorWindowResize = false; // turn off autosizing
41293 this.layout = config.layout;
41294 this.layout.getEl().addClass("roo-layout-nested-layout");
41295 this.layout.parent = this;
41302 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41304 setSize : function(width, height){
41305 if(!this.ignoreResize(width, height)){
41306 var size = this.adjustForComponents(width, height);
41307 var el = this.layout.getEl();
41308 if (size.height < 1) {
41309 el.setWidth(size.width);
41311 el.setSize(size.width, size.height);
41313 var touch = el.dom.offsetWidth;
41314 this.layout.layout();
41315 // ie requires a double layout on the first pass
41316 if(Roo.isIE && !this.initialized){
41317 this.initialized = true;
41318 this.layout.layout();
41323 // activate all subpanels if not currently active..
41325 setActiveState : function(active){
41326 this.active = active;
41327 this.setActiveClass(active);
41330 this.fireEvent("deactivate", this);
41334 this.fireEvent("activate", this);
41335 // not sure if this should happen before or after..
41336 if (!this.layout) {
41337 return; // should not happen..
41340 for (var r in this.layout.regions) {
41341 reg = this.layout.getRegion(r);
41342 if (reg.getActivePanel()) {
41343 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41344 reg.setActivePanel(reg.getActivePanel());
41347 if (!reg.panels.length) {
41350 reg.showPanel(reg.getPanel(0));
41359 * Returns the nested BorderLayout for this panel
41360 * @return {Roo.BorderLayout}
41362 getLayout : function(){
41363 return this.layout;
41367 * Adds a xtype elements to the layout of the nested panel
41371 xtype : 'ContentPanel',
41378 xtype : 'NestedLayoutPanel',
41384 items : [ ... list of content panels or nested layout panels.. ]
41388 * @param {Object} cfg Xtype definition of item to add.
41390 addxtype : function(cfg) {
41391 return this.layout.addxtype(cfg);
41396 * Ext JS Library 1.1.1
41397 * Copyright(c) 2006-2007, Ext JS, LLC.
41399 * Originally Released Under LGPL - original licence link has changed is not relivant.
41402 * <script type="text/javascript">
41405 * @class Roo.TabPanel
41406 * @extends Roo.util.Observable
41407 * A lightweight tab container.
41411 // basic tabs 1, built from existing content
41412 var tabs = new Roo.TabPanel("tabs1");
41413 tabs.addTab("script", "View Script");
41414 tabs.addTab("markup", "View Markup");
41415 tabs.activate("script");
41417 // more advanced tabs, built from javascript
41418 var jtabs = new Roo.TabPanel("jtabs");
41419 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41421 // set up the UpdateManager
41422 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41423 var updater = tab2.getUpdateManager();
41424 updater.setDefaultUrl("ajax1.htm");
41425 tab2.on('activate', updater.refresh, updater, true);
41427 // Use setUrl for Ajax loading
41428 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41429 tab3.setUrl("ajax2.htm", null, true);
41432 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41435 jtabs.activate("jtabs-1");
41438 * Create a new TabPanel.
41439 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41440 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41442 Roo.bootstrap.panel.Tabs = function(config){
41444 * The container element for this TabPanel.
41445 * @type Roo.Element
41447 this.el = Roo.get(config.el);
41450 if(typeof config == "boolean"){
41451 this.tabPosition = config ? "bottom" : "top";
41453 Roo.apply(this, config);
41457 if(this.tabPosition == "bottom"){
41458 // if tabs are at the bottom = create the body first.
41459 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41460 this.el.addClass("roo-tabs-bottom");
41462 // next create the tabs holders
41464 if (this.tabPosition == "west"){
41466 var reg = this.region; // fake it..
41468 if (!reg.mgr.parent) {
41471 reg = reg.mgr.parent.region;
41473 Roo.log("got nest?");
41475 if (reg.mgr.getRegion('west')) {
41476 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41477 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41478 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41479 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41480 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41488 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41489 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41490 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41491 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41496 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41499 // finally - if tabs are at the top, then create the body last..
41500 if(this.tabPosition != "bottom"){
41501 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41502 * @type Roo.Element
41504 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41505 this.el.addClass("roo-tabs-top");
41509 this.bodyEl.setStyle("position", "relative");
41511 this.active = null;
41512 this.activateDelegate = this.activate.createDelegate(this);
41517 * Fires when the active tab changes
41518 * @param {Roo.TabPanel} this
41519 * @param {Roo.TabPanelItem} activePanel The new active tab
41523 * @event beforetabchange
41524 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41525 * @param {Roo.TabPanel} this
41526 * @param {Object} e Set cancel to true on this object to cancel the tab change
41527 * @param {Roo.TabPanelItem} tab The tab being changed to
41529 "beforetabchange" : true
41532 Roo.EventManager.onWindowResize(this.onResize, this);
41533 this.cpad = this.el.getPadding("lr");
41534 this.hiddenCount = 0;
41537 // toolbar on the tabbar support...
41538 if (this.toolbar) {
41539 alert("no toolbar support yet");
41540 this.toolbar = false;
41542 var tcfg = this.toolbar;
41543 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41544 this.toolbar = new Roo.Toolbar(tcfg);
41545 if (Roo.isSafari) {
41546 var tbl = tcfg.container.child('table', true);
41547 tbl.setAttribute('width', '100%');
41555 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41558 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41560 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41562 tabPosition : "top",
41564 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41566 currentTabWidth : 0,
41568 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41572 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41576 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41578 preferredTabWidth : 175,
41580 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41582 resizeTabs : false,
41584 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41586 monitorResize : true,
41588 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41590 toolbar : false, // set by caller..
41592 region : false, /// set by caller
41594 disableTooltips : true, // not used yet...
41597 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41598 * @param {String} id The id of the div to use <b>or create</b>
41599 * @param {String} text The text for the tab
41600 * @param {String} content (optional) Content to put in the TabPanelItem body
41601 * @param {Boolean} closable (optional) True to create a close icon on the tab
41602 * @return {Roo.TabPanelItem} The created TabPanelItem
41604 addTab : function(id, text, content, closable, tpl)
41606 var item = new Roo.bootstrap.panel.TabItem({
41610 closable : closable,
41613 this.addTabItem(item);
41615 item.setContent(content);
41621 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41622 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41623 * @return {Roo.TabPanelItem}
41625 getTab : function(id){
41626 return this.items[id];
41630 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41631 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41633 hideTab : function(id){
41634 var t = this.items[id];
41637 this.hiddenCount++;
41638 this.autoSizeTabs();
41643 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41644 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41646 unhideTab : function(id){
41647 var t = this.items[id];
41649 t.setHidden(false);
41650 this.hiddenCount--;
41651 this.autoSizeTabs();
41656 * Adds an existing {@link Roo.TabPanelItem}.
41657 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41659 addTabItem : function(item)
41661 this.items[item.id] = item;
41662 this.items.push(item);
41663 this.autoSizeTabs();
41664 // if(this.resizeTabs){
41665 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41666 // this.autoSizeTabs();
41668 // item.autoSize();
41673 * Removes a {@link Roo.TabPanelItem}.
41674 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41676 removeTab : function(id){
41677 var items = this.items;
41678 var tab = items[id];
41679 if(!tab) { return; }
41680 var index = items.indexOf(tab);
41681 if(this.active == tab && items.length > 1){
41682 var newTab = this.getNextAvailable(index);
41687 this.stripEl.dom.removeChild(tab.pnode.dom);
41688 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41689 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41691 items.splice(index, 1);
41692 delete this.items[tab.id];
41693 tab.fireEvent("close", tab);
41694 tab.purgeListeners();
41695 this.autoSizeTabs();
41698 getNextAvailable : function(start){
41699 var items = this.items;
41701 // look for a next tab that will slide over to
41702 // replace the one being removed
41703 while(index < items.length){
41704 var item = items[++index];
41705 if(item && !item.isHidden()){
41709 // if one isn't found select the previous tab (on the left)
41712 var item = items[--index];
41713 if(item && !item.isHidden()){
41721 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41722 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41724 disableTab : function(id){
41725 var tab = this.items[id];
41726 if(tab && this.active != tab){
41732 * Enables a {@link Roo.TabPanelItem} that is disabled.
41733 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41735 enableTab : function(id){
41736 var tab = this.items[id];
41741 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41742 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41743 * @return {Roo.TabPanelItem} The TabPanelItem.
41745 activate : function(id)
41747 //Roo.log('activite:' + id);
41749 var tab = this.items[id];
41753 if(tab == this.active || tab.disabled){
41757 this.fireEvent("beforetabchange", this, e, tab);
41758 if(e.cancel !== true && !tab.disabled){
41760 this.active.hide();
41762 this.active = this.items[id];
41763 this.active.show();
41764 this.fireEvent("tabchange", this, this.active);
41770 * Gets the active {@link Roo.TabPanelItem}.
41771 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41773 getActiveTab : function(){
41774 return this.active;
41778 * Updates the tab body element to fit the height of the container element
41779 * for overflow scrolling
41780 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41782 syncHeight : function(targetHeight){
41783 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41784 var bm = this.bodyEl.getMargins();
41785 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41786 this.bodyEl.setHeight(newHeight);
41790 onResize : function(){
41791 if(this.monitorResize){
41792 this.autoSizeTabs();
41797 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41799 beginUpdate : function(){
41800 this.updating = true;
41804 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41806 endUpdate : function(){
41807 this.updating = false;
41808 this.autoSizeTabs();
41812 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41814 autoSizeTabs : function()
41816 var count = this.items.length;
41817 var vcount = count - this.hiddenCount;
41820 this.stripEl.hide();
41822 this.stripEl.show();
41825 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41830 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41831 var availWidth = Math.floor(w / vcount);
41832 var b = this.stripBody;
41833 if(b.getWidth() > w){
41834 var tabs = this.items;
41835 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41836 if(availWidth < this.minTabWidth){
41837 /*if(!this.sleft){ // incomplete scrolling code
41838 this.createScrollButtons();
41841 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41844 if(this.currentTabWidth < this.preferredTabWidth){
41845 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41851 * Returns the number of tabs in this TabPanel.
41854 getCount : function(){
41855 return this.items.length;
41859 * Resizes all the tabs to the passed width
41860 * @param {Number} The new width
41862 setTabWidth : function(width){
41863 this.currentTabWidth = width;
41864 for(var i = 0, len = this.items.length; i < len; i++) {
41865 if(!this.items[i].isHidden()) {
41866 this.items[i].setWidth(width);
41872 * Destroys this TabPanel
41873 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41875 destroy : function(removeEl){
41876 Roo.EventManager.removeResizeListener(this.onResize, this);
41877 for(var i = 0, len = this.items.length; i < len; i++){
41878 this.items[i].purgeListeners();
41880 if(removeEl === true){
41881 this.el.update("");
41886 createStrip : function(container)
41888 var strip = document.createElement("nav");
41889 strip.className = Roo.bootstrap.version == 4 ?
41890 "navbar-light bg-light" :
41891 "navbar navbar-default"; //"x-tabs-wrap";
41892 container.appendChild(strip);
41896 createStripList : function(strip)
41898 // div wrapper for retard IE
41899 // returns the "tr" element.
41900 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41901 //'<div class="x-tabs-strip-wrap">'+
41902 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41903 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41904 return strip.firstChild; //.firstChild.firstChild.firstChild;
41906 createBody : function(container)
41908 var body = document.createElement("div");
41909 Roo.id(body, "tab-body");
41910 //Roo.fly(body).addClass("x-tabs-body");
41911 Roo.fly(body).addClass("tab-content");
41912 container.appendChild(body);
41915 createItemBody :function(bodyEl, id){
41916 var body = Roo.getDom(id);
41918 body = document.createElement("div");
41921 //Roo.fly(body).addClass("x-tabs-item-body");
41922 Roo.fly(body).addClass("tab-pane");
41923 bodyEl.insertBefore(body, bodyEl.firstChild);
41927 createStripElements : function(stripEl, text, closable, tpl)
41929 var td = document.createElement("li"); // was td..
41930 td.className = 'nav-item';
41932 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41935 stripEl.appendChild(td);
41937 td.className = "x-tabs-closable";
41938 if(!this.closeTpl){
41939 this.closeTpl = new Roo.Template(
41940 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41941 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41942 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41945 var el = this.closeTpl.overwrite(td, {"text": text});
41946 var close = el.getElementsByTagName("div")[0];
41947 var inner = el.getElementsByTagName("em")[0];
41948 return {"el": el, "close": close, "inner": inner};
41951 // not sure what this is..
41952 // if(!this.tabTpl){
41953 //this.tabTpl = new Roo.Template(
41954 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41955 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41957 // this.tabTpl = new Roo.Template(
41958 // '<a href="#">' +
41959 // '<span unselectable="on"' +
41960 // (this.disableTooltips ? '' : ' title="{text}"') +
41961 // ' >{text}</span></a>'
41967 var template = tpl || this.tabTpl || false;
41970 template = new Roo.Template(
41971 Roo.bootstrap.version == 4 ?
41973 '<a class="nav-link" href="#" unselectable="on"' +
41974 (this.disableTooltips ? '' : ' title="{text}"') +
41977 '<a class="nav-link" href="#">' +
41978 '<span unselectable="on"' +
41979 (this.disableTooltips ? '' : ' title="{text}"') +
41980 ' >{text}</span></a>'
41985 switch (typeof(template)) {
41989 template = new Roo.Template(template);
41995 var el = template.overwrite(td, {"text": text});
41997 var inner = el.getElementsByTagName("span")[0];
41999 return {"el": el, "inner": inner};
42007 * @class Roo.TabPanelItem
42008 * @extends Roo.util.Observable
42009 * Represents an individual item (tab plus body) in a TabPanel.
42010 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42011 * @param {String} id The id of this TabPanelItem
42012 * @param {String} text The text for the tab of this TabPanelItem
42013 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42015 Roo.bootstrap.panel.TabItem = function(config){
42017 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42018 * @type Roo.TabPanel
42020 this.tabPanel = config.panel;
42022 * The id for this TabPanelItem
42025 this.id = config.id;
42027 this.disabled = false;
42029 this.text = config.text;
42031 this.loaded = false;
42032 this.closable = config.closable;
42035 * The body element for this TabPanelItem.
42036 * @type Roo.Element
42038 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42039 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42040 this.bodyEl.setStyle("display", "block");
42041 this.bodyEl.setStyle("zoom", "1");
42042 //this.hideAction();
42044 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42046 this.el = Roo.get(els.el);
42047 this.inner = Roo.get(els.inner, true);
42048 this.textEl = Roo.bootstrap.version == 4 ?
42049 this.el : Roo.get(this.el.dom.firstChild, true);
42051 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42052 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42055 // this.el.on("mousedown", this.onTabMouseDown, this);
42056 this.el.on("click", this.onTabClick, this);
42058 if(config.closable){
42059 var c = Roo.get(els.close, true);
42060 c.dom.title = this.closeText;
42061 c.addClassOnOver("close-over");
42062 c.on("click", this.closeClick, this);
42068 * Fires when this tab becomes the active tab.
42069 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42070 * @param {Roo.TabPanelItem} this
42074 * @event beforeclose
42075 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42076 * @param {Roo.TabPanelItem} this
42077 * @param {Object} e Set cancel to true on this object to cancel the close.
42079 "beforeclose": true,
42082 * Fires when this tab is closed.
42083 * @param {Roo.TabPanelItem} this
42087 * @event deactivate
42088 * Fires when this tab is no longer the active tab.
42089 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42090 * @param {Roo.TabPanelItem} this
42092 "deactivate" : true
42094 this.hidden = false;
42096 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42099 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42101 purgeListeners : function(){
42102 Roo.util.Observable.prototype.purgeListeners.call(this);
42103 this.el.removeAllListeners();
42106 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42109 this.status_node.addClass("active");
42112 this.tabPanel.stripWrap.repaint();
42114 this.fireEvent("activate", this.tabPanel, this);
42118 * Returns true if this tab is the active tab.
42119 * @return {Boolean}
42121 isActive : function(){
42122 return this.tabPanel.getActiveTab() == this;
42126 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42129 this.status_node.removeClass("active");
42131 this.fireEvent("deactivate", this.tabPanel, this);
42134 hideAction : function(){
42135 this.bodyEl.hide();
42136 this.bodyEl.setStyle("position", "absolute");
42137 this.bodyEl.setLeft("-20000px");
42138 this.bodyEl.setTop("-20000px");
42141 showAction : function(){
42142 this.bodyEl.setStyle("position", "relative");
42143 this.bodyEl.setTop("");
42144 this.bodyEl.setLeft("");
42145 this.bodyEl.show();
42149 * Set the tooltip for the tab.
42150 * @param {String} tooltip The tab's tooltip
42152 setTooltip : function(text){
42153 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42154 this.textEl.dom.qtip = text;
42155 this.textEl.dom.removeAttribute('title');
42157 this.textEl.dom.title = text;
42161 onTabClick : function(e){
42162 e.preventDefault();
42163 this.tabPanel.activate(this.id);
42166 onTabMouseDown : function(e){
42167 e.preventDefault();
42168 this.tabPanel.activate(this.id);
42171 getWidth : function(){
42172 return this.inner.getWidth();
42175 setWidth : function(width){
42176 var iwidth = width - this.linode.getPadding("lr");
42177 this.inner.setWidth(iwidth);
42178 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42179 this.linode.setWidth(width);
42183 * Show or hide the tab
42184 * @param {Boolean} hidden True to hide or false to show.
42186 setHidden : function(hidden){
42187 this.hidden = hidden;
42188 this.linode.setStyle("display", hidden ? "none" : "");
42192 * Returns true if this tab is "hidden"
42193 * @return {Boolean}
42195 isHidden : function(){
42196 return this.hidden;
42200 * Returns the text for this tab
42203 getText : function(){
42207 autoSize : function(){
42208 //this.el.beginMeasure();
42209 this.textEl.setWidth(1);
42211 * #2804 [new] Tabs in Roojs
42212 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42214 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42215 //this.el.endMeasure();
42219 * Sets the text for the tab (Note: this also sets the tooltip text)
42220 * @param {String} text The tab's text and tooltip
42222 setText : function(text){
42224 this.textEl.update(text);
42225 this.setTooltip(text);
42226 //if(!this.tabPanel.resizeTabs){
42227 // this.autoSize();
42231 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42233 activate : function(){
42234 this.tabPanel.activate(this.id);
42238 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42240 disable : function(){
42241 if(this.tabPanel.active != this){
42242 this.disabled = true;
42243 this.status_node.addClass("disabled");
42248 * Enables this TabPanelItem if it was previously disabled.
42250 enable : function(){
42251 this.disabled = false;
42252 this.status_node.removeClass("disabled");
42256 * Sets the content for this TabPanelItem.
42257 * @param {String} content The content
42258 * @param {Boolean} loadScripts true to look for and load scripts
42260 setContent : function(content, loadScripts){
42261 this.bodyEl.update(content, loadScripts);
42265 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42266 * @return {Roo.UpdateManager} The UpdateManager
42268 getUpdateManager : function(){
42269 return this.bodyEl.getUpdateManager();
42273 * Set a URL to be used to load the content for this TabPanelItem.
42274 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42275 * @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)
42276 * @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)
42277 * @return {Roo.UpdateManager} The UpdateManager
42279 setUrl : function(url, params, loadOnce){
42280 if(this.refreshDelegate){
42281 this.un('activate', this.refreshDelegate);
42283 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42284 this.on("activate", this.refreshDelegate);
42285 return this.bodyEl.getUpdateManager();
42289 _handleRefresh : function(url, params, loadOnce){
42290 if(!loadOnce || !this.loaded){
42291 var updater = this.bodyEl.getUpdateManager();
42292 updater.update(url, params, this._setLoaded.createDelegate(this));
42297 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42298 * Will fail silently if the setUrl method has not been called.
42299 * This does not activate the panel, just updates its content.
42301 refresh : function(){
42302 if(this.refreshDelegate){
42303 this.loaded = false;
42304 this.refreshDelegate();
42309 _setLoaded : function(){
42310 this.loaded = true;
42314 closeClick : function(e){
42317 this.fireEvent("beforeclose", this, o);
42318 if(o.cancel !== true){
42319 this.tabPanel.removeTab(this.id);
42323 * The text displayed in the tooltip for the close icon.
42326 closeText : "Close this tab"
42329 * This script refer to:
42330 * Title: International Telephone Input
42331 * Author: Jack O'Connor
42332 * Code version: v12.1.12
42333 * Availability: https://github.com/jackocnr/intl-tel-input.git
42336 Roo.bootstrap.PhoneInputData = function() {
42339 "Afghanistan (افغانستان)",
42344 "Albania (Shqipëri)",
42349 "Algeria (الجزائر)",
42374 "Antigua and Barbuda",
42384 "Armenia (Հայաստան)",
42400 "Austria (Österreich)",
42405 "Azerbaijan (Azərbaycan)",
42415 "Bahrain (البحرين)",
42420 "Bangladesh (বাংলাদেশ)",
42430 "Belarus (Беларусь)",
42435 "Belgium (België)",
42465 "Bosnia and Herzegovina (Босна и Херцеговина)",
42480 "British Indian Ocean Territory",
42485 "British Virgin Islands",
42495 "Bulgaria (България)",
42505 "Burundi (Uburundi)",
42510 "Cambodia (កម្ពុជា)",
42515 "Cameroon (Cameroun)",
42524 ["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"]
42527 "Cape Verde (Kabu Verdi)",
42532 "Caribbean Netherlands",
42543 "Central African Republic (République centrafricaine)",
42563 "Christmas Island",
42569 "Cocos (Keeling) Islands",
42580 "Comoros (جزر القمر)",
42585 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42590 "Congo (Republic) (Congo-Brazzaville)",
42610 "Croatia (Hrvatska)",
42631 "Czech Republic (Česká republika)",
42636 "Denmark (Danmark)",
42651 "Dominican Republic (República Dominicana)",
42655 ["809", "829", "849"]
42673 "Equatorial Guinea (Guinea Ecuatorial)",
42693 "Falkland Islands (Islas Malvinas)",
42698 "Faroe Islands (Føroyar)",
42719 "French Guiana (Guyane française)",
42724 "French Polynesia (Polynésie française)",
42739 "Georgia (საქართველო)",
42744 "Germany (Deutschland)",
42764 "Greenland (Kalaallit Nunaat)",
42801 "Guinea-Bissau (Guiné Bissau)",
42826 "Hungary (Magyarország)",
42831 "Iceland (Ísland)",
42851 "Iraq (العراق)",
42867 "Israel (ישראל)",
42894 "Jordan (الأردن)",
42899 "Kazakhstan (Казахстан)",
42920 "Kuwait (الكويت)",
42925 "Kyrgyzstan (Кыргызстан)",
42935 "Latvia (Latvija)",
42940 "Lebanon (لبنان)",
42955 "Libya (ليبيا)",
42965 "Lithuania (Lietuva)",
42980 "Macedonia (FYROM) (Македонија)",
42985 "Madagascar (Madagasikara)",
43015 "Marshall Islands",
43025 "Mauritania (موريتانيا)",
43030 "Mauritius (Moris)",
43051 "Moldova (Republica Moldova)",
43061 "Mongolia (Монгол)",
43066 "Montenegro (Crna Gora)",
43076 "Morocco (المغرب)",
43082 "Mozambique (Moçambique)",
43087 "Myanmar (Burma) (မြန်မာ)",
43092 "Namibia (Namibië)",
43107 "Netherlands (Nederland)",
43112 "New Caledonia (Nouvelle-Calédonie)",
43147 "North Korea (조선 민주주의 인민 공화국)",
43152 "Northern Mariana Islands",
43168 "Pakistan (پاکستان)",
43178 "Palestine (فلسطين)",
43188 "Papua New Guinea",
43230 "Réunion (La Réunion)",
43236 "Romania (România)",
43252 "Saint Barthélemy",
43263 "Saint Kitts and Nevis",
43273 "Saint Martin (Saint-Martin (partie française))",
43279 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43284 "Saint Vincent and the Grenadines",
43299 "São Tomé and Príncipe (São Tomé e Príncipe)",
43304 "Saudi Arabia (المملكة العربية السعودية)",
43309 "Senegal (Sénégal)",
43339 "Slovakia (Slovensko)",
43344 "Slovenia (Slovenija)",
43354 "Somalia (Soomaaliya)",
43364 "South Korea (대한민국)",
43369 "South Sudan (جنوب السودان)",
43379 "Sri Lanka (ශ්රී ලංකාව)",
43384 "Sudan (السودان)",
43394 "Svalbard and Jan Mayen",
43405 "Sweden (Sverige)",
43410 "Switzerland (Schweiz)",
43415 "Syria (سوريا)",
43460 "Trinidad and Tobago",
43465 "Tunisia (تونس)",
43470 "Turkey (Türkiye)",
43480 "Turks and Caicos Islands",
43490 "U.S. Virgin Islands",
43500 "Ukraine (Україна)",
43505 "United Arab Emirates (الإمارات العربية المتحدة)",
43527 "Uzbekistan (Oʻzbekiston)",
43537 "Vatican City (Città del Vaticano)",
43548 "Vietnam (Việt Nam)",
43553 "Wallis and Futuna (Wallis-et-Futuna)",
43558 "Western Sahara (الصحراء الغربية)",
43564 "Yemen (اليمن)",
43588 * This script refer to:
43589 * Title: International Telephone Input
43590 * Author: Jack O'Connor
43591 * Code version: v12.1.12
43592 * Availability: https://github.com/jackocnr/intl-tel-input.git
43596 * @class Roo.bootstrap.PhoneInput
43597 * @extends Roo.bootstrap.TriggerField
43598 * An input with International dial-code selection
43600 * @cfg {String} defaultDialCode default '+852'
43601 * @cfg {Array} preferedCountries default []
43604 * Create a new PhoneInput.
43605 * @param {Object} config Configuration options
43608 Roo.bootstrap.PhoneInput = function(config) {
43609 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43612 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43614 listWidth: undefined,
43616 selectedClass: 'active',
43618 invalidClass : "has-warning",
43620 validClass: 'has-success',
43622 allowed: '0123456789',
43627 * @cfg {String} defaultDialCode The default dial code when initializing the input
43629 defaultDialCode: '+852',
43632 * @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
43634 preferedCountries: false,
43636 getAutoCreate : function()
43638 var data = Roo.bootstrap.PhoneInputData();
43639 var align = this.labelAlign || this.parentLabelAlign();
43642 this.allCountries = [];
43643 this.dialCodeMapping = [];
43645 for (var i = 0; i < data.length; i++) {
43647 this.allCountries[i] = {
43651 priority: c[3] || 0,
43652 areaCodes: c[4] || null
43654 this.dialCodeMapping[c[2]] = {
43657 priority: c[3] || 0,
43658 areaCodes: c[4] || null
43670 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43671 maxlength: this.max_length,
43672 cls : 'form-control tel-input',
43673 autocomplete: 'new-password'
43676 var hiddenInput = {
43679 cls: 'hidden-tel-input'
43683 hiddenInput.name = this.name;
43686 if (this.disabled) {
43687 input.disabled = true;
43690 var flag_container = {
43707 cls: this.hasFeedback ? 'has-feedback' : '',
43713 cls: 'dial-code-holder',
43720 cls: 'roo-select2-container input-group',
43727 if (this.fieldLabel.length) {
43730 tooltip: 'This field is required'
43736 cls: 'control-label',
43742 html: this.fieldLabel
43745 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43751 if(this.indicatorpos == 'right') {
43752 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43759 if(align == 'left') {
43767 if(this.labelWidth > 12){
43768 label.style = "width: " + this.labelWidth + 'px';
43770 if(this.labelWidth < 13 && this.labelmd == 0){
43771 this.labelmd = this.labelWidth;
43773 if(this.labellg > 0){
43774 label.cls += ' col-lg-' + this.labellg;
43775 input.cls += ' col-lg-' + (12 - this.labellg);
43777 if(this.labelmd > 0){
43778 label.cls += ' col-md-' + this.labelmd;
43779 container.cls += ' col-md-' + (12 - this.labelmd);
43781 if(this.labelsm > 0){
43782 label.cls += ' col-sm-' + this.labelsm;
43783 container.cls += ' col-sm-' + (12 - this.labelsm);
43785 if(this.labelxs > 0){
43786 label.cls += ' col-xs-' + this.labelxs;
43787 container.cls += ' col-xs-' + (12 - this.labelxs);
43797 var settings = this;
43799 ['xs','sm','md','lg'].map(function(size){
43800 if (settings[size]) {
43801 cfg.cls += ' col-' + size + '-' + settings[size];
43805 this.store = new Roo.data.Store({
43806 proxy : new Roo.data.MemoryProxy({}),
43807 reader : new Roo.data.JsonReader({
43818 'name' : 'dialCode',
43822 'name' : 'priority',
43826 'name' : 'areaCodes',
43833 if(!this.preferedCountries) {
43834 this.preferedCountries = [
43841 var p = this.preferedCountries.reverse();
43844 for (var i = 0; i < p.length; i++) {
43845 for (var j = 0; j < this.allCountries.length; j++) {
43846 if(this.allCountries[j].iso2 == p[i]) {
43847 var t = this.allCountries[j];
43848 this.allCountries.splice(j,1);
43849 this.allCountries.unshift(t);
43855 this.store.proxy.data = {
43857 data: this.allCountries
43863 initEvents : function()
43866 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43868 this.indicator = this.indicatorEl();
43869 this.flag = this.flagEl();
43870 this.dialCodeHolder = this.dialCodeHolderEl();
43872 this.trigger = this.el.select('div.flag-box',true).first();
43873 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43878 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43879 _this.list.setWidth(lw);
43882 this.list.on('mouseover', this.onViewOver, this);
43883 this.list.on('mousemove', this.onViewMove, this);
43884 this.inputEl().on("keyup", this.onKeyUp, this);
43885 this.inputEl().on("keypress", this.onKeyPress, this);
43887 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43889 this.view = new Roo.View(this.list, this.tpl, {
43890 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43893 this.view.on('click', this.onViewClick, this);
43894 this.setValue(this.defaultDialCode);
43897 onTriggerClick : function(e)
43899 Roo.log('trigger click');
43904 if(this.isExpanded()){
43906 this.hasFocus = false;
43908 this.store.load({});
43909 this.hasFocus = true;
43914 isExpanded : function()
43916 return this.list.isVisible();
43919 collapse : function()
43921 if(!this.isExpanded()){
43925 Roo.get(document).un('mousedown', this.collapseIf, this);
43926 Roo.get(document).un('mousewheel', this.collapseIf, this);
43927 this.fireEvent('collapse', this);
43931 expand : function()
43935 if(this.isExpanded() || !this.hasFocus){
43939 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43940 this.list.setWidth(lw);
43943 this.restrictHeight();
43945 Roo.get(document).on('mousedown', this.collapseIf, this);
43946 Roo.get(document).on('mousewheel', this.collapseIf, this);
43948 this.fireEvent('expand', this);
43951 restrictHeight : function()
43953 this.list.alignTo(this.inputEl(), this.listAlign);
43954 this.list.alignTo(this.inputEl(), this.listAlign);
43957 onViewOver : function(e, t)
43959 if(this.inKeyMode){
43962 var item = this.view.findItemFromChild(t);
43965 var index = this.view.indexOf(item);
43966 this.select(index, false);
43971 onViewClick : function(view, doFocus, el, e)
43973 var index = this.view.getSelectedIndexes()[0];
43975 var r = this.store.getAt(index);
43978 this.onSelect(r, index);
43980 if(doFocus !== false && !this.blockFocus){
43981 this.inputEl().focus();
43985 onViewMove : function(e, t)
43987 this.inKeyMode = false;
43990 select : function(index, scrollIntoView)
43992 this.selectedIndex = index;
43993 this.view.select(index);
43994 if(scrollIntoView !== false){
43995 var el = this.view.getNode(index);
43997 this.list.scrollChildIntoView(el, false);
44002 createList : function()
44004 this.list = Roo.get(document.body).createChild({
44006 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44007 style: 'display:none'
44010 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44013 collapseIf : function(e)
44015 var in_combo = e.within(this.el);
44016 var in_list = e.within(this.list);
44017 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44019 if (in_combo || in_list || is_list) {
44025 onSelect : function(record, index)
44027 if(this.fireEvent('beforeselect', this, record, index) !== false){
44029 this.setFlagClass(record.data.iso2);
44030 this.setDialCode(record.data.dialCode);
44031 this.hasFocus = false;
44033 this.fireEvent('select', this, record, index);
44037 flagEl : function()
44039 var flag = this.el.select('div.flag',true).first();
44046 dialCodeHolderEl : function()
44048 var d = this.el.select('input.dial-code-holder',true).first();
44055 setDialCode : function(v)
44057 this.dialCodeHolder.dom.value = '+'+v;
44060 setFlagClass : function(n)
44062 this.flag.dom.className = 'flag '+n;
44065 getValue : function()
44067 var v = this.inputEl().getValue();
44068 if(this.dialCodeHolder) {
44069 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44074 setValue : function(v)
44076 var d = this.getDialCode(v);
44078 //invalid dial code
44079 if(v.length == 0 || !d || d.length == 0) {
44081 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44082 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44088 this.setFlagClass(this.dialCodeMapping[d].iso2);
44089 this.setDialCode(d);
44090 this.inputEl().dom.value = v.replace('+'+d,'');
44091 this.hiddenEl().dom.value = this.getValue();
44096 getDialCode : function(v)
44100 if (v.length == 0) {
44101 return this.dialCodeHolder.dom.value;
44105 if (v.charAt(0) != "+") {
44108 var numericChars = "";
44109 for (var i = 1; i < v.length; i++) {
44110 var c = v.charAt(i);
44113 if (this.dialCodeMapping[numericChars]) {
44114 dialCode = v.substr(1, i);
44116 if (numericChars.length == 4) {
44126 this.setValue(this.defaultDialCode);
44130 hiddenEl : function()
44132 return this.el.select('input.hidden-tel-input',true).first();
44135 // after setting val
44136 onKeyUp : function(e){
44137 this.setValue(this.getValue());
44140 onKeyPress : function(e){
44141 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44148 * @class Roo.bootstrap.MoneyField
44149 * @extends Roo.bootstrap.ComboBox
44150 * Bootstrap MoneyField class
44153 * Create a new MoneyField.
44154 * @param {Object} config Configuration options
44157 Roo.bootstrap.MoneyField = function(config) {
44159 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44163 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44166 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44168 allowDecimals : true,
44170 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44172 decimalSeparator : ".",
44174 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44176 decimalPrecision : 0,
44178 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44180 allowNegative : true,
44182 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44186 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44188 minValue : Number.NEGATIVE_INFINITY,
44190 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44192 maxValue : Number.MAX_VALUE,
44194 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44196 minText : "The minimum value for this field is {0}",
44198 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44200 maxText : "The maximum value for this field is {0}",
44202 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44203 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44205 nanText : "{0} is not a valid number",
44207 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44211 * @cfg {String} defaults currency of the MoneyField
44212 * value should be in lkey
44214 defaultCurrency : false,
44216 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44218 thousandsDelimiter : false,
44220 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44231 getAutoCreate : function()
44233 var align = this.labelAlign || this.parentLabelAlign();
44245 cls : 'form-control roo-money-amount-input',
44246 autocomplete: 'new-password'
44249 var hiddenInput = {
44253 cls: 'hidden-number-input'
44256 if(this.max_length) {
44257 input.maxlength = this.max_length;
44261 hiddenInput.name = this.name;
44264 if (this.disabled) {
44265 input.disabled = true;
44268 var clg = 12 - this.inputlg;
44269 var cmd = 12 - this.inputmd;
44270 var csm = 12 - this.inputsm;
44271 var cxs = 12 - this.inputxs;
44275 cls : 'row roo-money-field',
44279 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44283 cls: 'roo-select2-container input-group',
44287 cls : 'form-control roo-money-currency-input',
44288 autocomplete: 'new-password',
44290 name : this.currencyName
44294 cls : 'input-group-addon',
44308 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44312 cls: this.hasFeedback ? 'has-feedback' : '',
44323 if (this.fieldLabel.length) {
44326 tooltip: 'This field is required'
44332 cls: 'control-label',
44338 html: this.fieldLabel
44341 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44347 if(this.indicatorpos == 'right') {
44348 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44355 if(align == 'left') {
44363 if(this.labelWidth > 12){
44364 label.style = "width: " + this.labelWidth + 'px';
44366 if(this.labelWidth < 13 && this.labelmd == 0){
44367 this.labelmd = this.labelWidth;
44369 if(this.labellg > 0){
44370 label.cls += ' col-lg-' + this.labellg;
44371 input.cls += ' col-lg-' + (12 - this.labellg);
44373 if(this.labelmd > 0){
44374 label.cls += ' col-md-' + this.labelmd;
44375 container.cls += ' col-md-' + (12 - this.labelmd);
44377 if(this.labelsm > 0){
44378 label.cls += ' col-sm-' + this.labelsm;
44379 container.cls += ' col-sm-' + (12 - this.labelsm);
44381 if(this.labelxs > 0){
44382 label.cls += ' col-xs-' + this.labelxs;
44383 container.cls += ' col-xs-' + (12 - this.labelxs);
44394 var settings = this;
44396 ['xs','sm','md','lg'].map(function(size){
44397 if (settings[size]) {
44398 cfg.cls += ' col-' + size + '-' + settings[size];
44405 initEvents : function()
44407 this.indicator = this.indicatorEl();
44409 this.initCurrencyEvent();
44411 this.initNumberEvent();
44414 initCurrencyEvent : function()
44417 throw "can not find store for combo";
44420 this.store = Roo.factory(this.store, Roo.data);
44421 this.store.parent = this;
44425 this.triggerEl = this.el.select('.input-group-addon', true).first();
44427 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44432 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44433 _this.list.setWidth(lw);
44436 this.list.on('mouseover', this.onViewOver, this);
44437 this.list.on('mousemove', this.onViewMove, this);
44438 this.list.on('scroll', this.onViewScroll, this);
44441 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44444 this.view = new Roo.View(this.list, this.tpl, {
44445 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44448 this.view.on('click', this.onViewClick, this);
44450 this.store.on('beforeload', this.onBeforeLoad, this);
44451 this.store.on('load', this.onLoad, this);
44452 this.store.on('loadexception', this.onLoadException, this);
44454 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44455 "up" : function(e){
44456 this.inKeyMode = true;
44460 "down" : function(e){
44461 if(!this.isExpanded()){
44462 this.onTriggerClick();
44464 this.inKeyMode = true;
44469 "enter" : function(e){
44472 if(this.fireEvent("specialkey", this, e)){
44473 this.onViewClick(false);
44479 "esc" : function(e){
44483 "tab" : function(e){
44486 if(this.fireEvent("specialkey", this, e)){
44487 this.onViewClick(false);
44495 doRelay : function(foo, bar, hname){
44496 if(hname == 'down' || this.scope.isExpanded()){
44497 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44505 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44509 initNumberEvent : function(e)
44511 this.inputEl().on("keydown" , this.fireKey, this);
44512 this.inputEl().on("focus", this.onFocus, this);
44513 this.inputEl().on("blur", this.onBlur, this);
44515 this.inputEl().relayEvent('keyup', this);
44517 if(this.indicator){
44518 this.indicator.addClass('invisible');
44521 this.originalValue = this.getValue();
44523 if(this.validationEvent == 'keyup'){
44524 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44525 this.inputEl().on('keyup', this.filterValidation, this);
44527 else if(this.validationEvent !== false){
44528 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44531 if(this.selectOnFocus){
44532 this.on("focus", this.preFocus, this);
44535 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44536 this.inputEl().on("keypress", this.filterKeys, this);
44538 this.inputEl().relayEvent('keypress', this);
44541 var allowed = "0123456789";
44543 if(this.allowDecimals){
44544 allowed += this.decimalSeparator;
44547 if(this.allowNegative){
44551 if(this.thousandsDelimiter) {
44555 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44557 var keyPress = function(e){
44559 var k = e.getKey();
44561 var c = e.getCharCode();
44564 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44565 allowed.indexOf(String.fromCharCode(c)) === -1
44571 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44575 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44580 this.inputEl().on("keypress", keyPress, this);
44584 onTriggerClick : function(e)
44591 this.loadNext = false;
44593 if(this.isExpanded()){
44598 this.hasFocus = true;
44600 if(this.triggerAction == 'all') {
44601 this.doQuery(this.allQuery, true);
44605 this.doQuery(this.getRawValue());
44608 getCurrency : function()
44610 var v = this.currencyEl().getValue();
44615 restrictHeight : function()
44617 this.list.alignTo(this.currencyEl(), this.listAlign);
44618 this.list.alignTo(this.currencyEl(), this.listAlign);
44621 onViewClick : function(view, doFocus, el, e)
44623 var index = this.view.getSelectedIndexes()[0];
44625 var r = this.store.getAt(index);
44628 this.onSelect(r, index);
44632 onSelect : function(record, index){
44634 if(this.fireEvent('beforeselect', this, record, index) !== false){
44636 this.setFromCurrencyData(index > -1 ? record.data : false);
44640 this.fireEvent('select', this, record, index);
44644 setFromCurrencyData : function(o)
44648 this.lastCurrency = o;
44650 if (this.currencyField) {
44651 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44653 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44656 this.lastSelectionText = currency;
44658 //setting default currency
44659 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44660 this.setCurrency(this.defaultCurrency);
44664 this.setCurrency(currency);
44667 setFromData : function(o)
44671 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44673 this.setFromCurrencyData(c);
44678 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44680 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44683 this.setValue(value);
44687 setCurrency : function(v)
44689 this.currencyValue = v;
44692 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44697 setValue : function(v)
44699 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44705 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44707 this.inputEl().dom.value = (v == '') ? '' :
44708 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44710 if(!this.allowZero && v === '0') {
44711 this.hiddenEl().dom.value = '';
44712 this.inputEl().dom.value = '';
44719 getRawValue : function()
44721 var v = this.inputEl().getValue();
44726 getValue : function()
44728 return this.fixPrecision(this.parseValue(this.getRawValue()));
44731 parseValue : function(value)
44733 if(this.thousandsDelimiter) {
44735 r = new RegExp(",", "g");
44736 value = value.replace(r, "");
44739 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44740 return isNaN(value) ? '' : value;
44744 fixPrecision : function(value)
44746 if(this.thousandsDelimiter) {
44748 r = new RegExp(",", "g");
44749 value = value.replace(r, "");
44752 var nan = isNaN(value);
44754 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44755 return nan ? '' : value;
44757 return parseFloat(value).toFixed(this.decimalPrecision);
44760 decimalPrecisionFcn : function(v)
44762 return Math.floor(v);
44765 validateValue : function(value)
44767 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44771 var num = this.parseValue(value);
44774 this.markInvalid(String.format(this.nanText, value));
44778 if(num < this.minValue){
44779 this.markInvalid(String.format(this.minText, this.minValue));
44783 if(num > this.maxValue){
44784 this.markInvalid(String.format(this.maxText, this.maxValue));
44791 validate : function()
44793 if(this.disabled || this.allowBlank){
44798 var currency = this.getCurrency();
44800 if(this.validateValue(this.getRawValue()) && currency.length){
44805 this.markInvalid();
44809 getName: function()
44814 beforeBlur : function()
44820 var v = this.parseValue(this.getRawValue());
44827 onBlur : function()
44831 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44832 //this.el.removeClass(this.focusClass);
44835 this.hasFocus = false;
44837 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44841 var v = this.getValue();
44843 if(String(v) !== String(this.startValue)){
44844 this.fireEvent('change', this, v, this.startValue);
44847 this.fireEvent("blur", this);
44850 inputEl : function()
44852 return this.el.select('.roo-money-amount-input', true).first();
44855 currencyEl : function()
44857 return this.el.select('.roo-money-currency-input', true).first();
44860 hiddenEl : function()
44862 return this.el.select('input.hidden-number-input',true).first();
44866 * @class Roo.bootstrap.BezierSignature
44867 * @extends Roo.bootstrap.Component
44868 * Bootstrap BezierSignature class
44869 * This script refer to:
44870 * Title: Signature Pad
44872 * Availability: https://github.com/szimek/signature_pad
44875 * Create a new BezierSignature
44876 * @param {Object} config The config object
44879 Roo.bootstrap.BezierSignature = function(config){
44880 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44886 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44893 mouse_btn_down: true,
44896 * @cfg {int} canvas height
44898 canvas_height: '200px',
44901 * @cfg {float|function} Radius of a single dot.
44906 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44911 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44916 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44921 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44926 * @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.
44928 bg_color: 'rgba(0, 0, 0, 0)',
44931 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44933 dot_color: 'black',
44936 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44938 velocity_filter_weight: 0.7,
44941 * @cfg {function} Callback when stroke begin.
44946 * @cfg {function} Callback when stroke end.
44950 getAutoCreate : function()
44952 var cls = 'roo-signature column';
44955 cls += ' ' + this.cls;
44965 for(var i = 0; i < col_sizes.length; i++) {
44966 if(this[col_sizes[i]]) {
44967 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44977 cls: 'roo-signature-body',
44981 cls: 'roo-signature-body-canvas',
44982 height: this.canvas_height,
44983 width: this.canvas_width
44990 style: 'display: none'
44998 initEvents: function()
45000 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45002 var canvas = this.canvasEl();
45004 // mouse && touch event swapping...
45005 canvas.dom.style.touchAction = 'none';
45006 canvas.dom.style.msTouchAction = 'none';
45008 this.mouse_btn_down = false;
45009 canvas.on('mousedown', this._handleMouseDown, this);
45010 canvas.on('mousemove', this._handleMouseMove, this);
45011 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45013 if (window.PointerEvent) {
45014 canvas.on('pointerdown', this._handleMouseDown, this);
45015 canvas.on('pointermove', this._handleMouseMove, this);
45016 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45019 if ('ontouchstart' in window) {
45020 canvas.on('touchstart', this._handleTouchStart, this);
45021 canvas.on('touchmove', this._handleTouchMove, this);
45022 canvas.on('touchend', this._handleTouchEnd, this);
45025 Roo.EventManager.onWindowResize(this.resize, this, true);
45027 // file input event
45028 this.fileEl().on('change', this.uploadImage, this);
45035 resize: function(){
45037 var canvas = this.canvasEl().dom;
45038 var ctx = this.canvasElCtx();
45039 var img_data = false;
45041 if(canvas.width > 0) {
45042 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45044 // setting canvas width will clean img data
45047 var style = window.getComputedStyle ?
45048 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45050 var padding_left = parseInt(style.paddingLeft) || 0;
45051 var padding_right = parseInt(style.paddingRight) || 0;
45053 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45056 ctx.putImageData(img_data, 0, 0);
45060 _handleMouseDown: function(e)
45062 if (e.browserEvent.which === 1) {
45063 this.mouse_btn_down = true;
45064 this.strokeBegin(e);
45068 _handleMouseMove: function (e)
45070 if (this.mouse_btn_down) {
45071 this.strokeMoveUpdate(e);
45075 _handleMouseUp: function (e)
45077 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45078 this.mouse_btn_down = false;
45083 _handleTouchStart: function (e) {
45085 e.preventDefault();
45086 if (e.browserEvent.targetTouches.length === 1) {
45087 // var touch = e.browserEvent.changedTouches[0];
45088 // this.strokeBegin(touch);
45090 this.strokeBegin(e); // assume e catching the correct xy...
45094 _handleTouchMove: function (e) {
45095 e.preventDefault();
45096 // var touch = event.targetTouches[0];
45097 // _this._strokeMoveUpdate(touch);
45098 this.strokeMoveUpdate(e);
45101 _handleTouchEnd: function (e) {
45102 var wasCanvasTouched = e.target === this.canvasEl().dom;
45103 if (wasCanvasTouched) {
45104 e.preventDefault();
45105 // var touch = event.changedTouches[0];
45106 // _this._strokeEnd(touch);
45111 reset: function () {
45112 this._lastPoints = [];
45113 this._lastVelocity = 0;
45114 this._lastWidth = (this.min_width + this.max_width) / 2;
45115 this.canvasElCtx().fillStyle = this.dot_color;
45118 strokeMoveUpdate: function(e)
45120 this.strokeUpdate(e);
45122 if (this.throttle) {
45123 this.throttleStroke(this.strokeUpdate, this.throttle);
45126 this.strokeUpdate(e);
45130 strokeBegin: function(e)
45132 var newPointGroup = {
45133 color: this.dot_color,
45137 if (typeof this.onBegin === 'function') {
45141 this.curve_data.push(newPointGroup);
45143 this.strokeUpdate(e);
45146 strokeUpdate: function(e)
45148 var rect = this.canvasEl().dom.getBoundingClientRect();
45149 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45150 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45151 var lastPoints = lastPointGroup.points;
45152 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45153 var isLastPointTooClose = lastPoint
45154 ? point.distanceTo(lastPoint) <= this.min_distance
45156 var color = lastPointGroup.color;
45157 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45158 var curve = this.addPoint(point);
45160 this.drawDot({color: color, point: point});
45163 this.drawCurve({color: color, curve: curve});
45173 strokeEnd: function(e)
45175 this.strokeUpdate(e);
45176 if (typeof this.onEnd === 'function') {
45181 addPoint: function (point) {
45182 var _lastPoints = this._lastPoints;
45183 _lastPoints.push(point);
45184 if (_lastPoints.length > 2) {
45185 if (_lastPoints.length === 3) {
45186 _lastPoints.unshift(_lastPoints[0]);
45188 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45189 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45190 _lastPoints.shift();
45196 calculateCurveWidths: function (startPoint, endPoint) {
45197 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45198 (1 - this.velocity_filter_weight) * this._lastVelocity;
45200 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45203 start: this._lastWidth
45206 this._lastVelocity = velocity;
45207 this._lastWidth = newWidth;
45211 drawDot: function (_a) {
45212 var color = _a.color, point = _a.point;
45213 var ctx = this.canvasElCtx();
45214 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45216 this.drawCurveSegment(point.x, point.y, width);
45218 ctx.fillStyle = color;
45222 drawCurve: function (_a) {
45223 var color = _a.color, curve = _a.curve;
45224 var ctx = this.canvasElCtx();
45225 var widthDelta = curve.endWidth - curve.startWidth;
45226 var drawSteps = Math.floor(curve.length()) * 2;
45228 ctx.fillStyle = color;
45229 for (var i = 0; i < drawSteps; i += 1) {
45230 var t = i / drawSteps;
45236 var x = uuu * curve.startPoint.x;
45237 x += 3 * uu * t * curve.control1.x;
45238 x += 3 * u * tt * curve.control2.x;
45239 x += ttt * curve.endPoint.x;
45240 var y = uuu * curve.startPoint.y;
45241 y += 3 * uu * t * curve.control1.y;
45242 y += 3 * u * tt * curve.control2.y;
45243 y += ttt * curve.endPoint.y;
45244 var width = curve.startWidth + ttt * widthDelta;
45245 this.drawCurveSegment(x, y, width);
45251 drawCurveSegment: function (x, y, width) {
45252 var ctx = this.canvasElCtx();
45254 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45255 this.is_empty = false;
45260 var ctx = this.canvasElCtx();
45261 var canvas = this.canvasEl().dom;
45262 ctx.fillStyle = this.bg_color;
45263 ctx.clearRect(0, 0, canvas.width, canvas.height);
45264 ctx.fillRect(0, 0, canvas.width, canvas.height);
45265 this.curve_data = [];
45267 this.is_empty = true;
45272 return this.el.select('input',true).first();
45275 canvasEl: function()
45277 return this.el.select('canvas',true).first();
45280 canvasElCtx: function()
45282 return this.el.select('canvas',true).first().dom.getContext('2d');
45285 getImage: function(type)
45287 if(this.is_empty) {
45292 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45295 drawFromImage: function(img_src)
45297 var img = new Image();
45299 img.onload = function(){
45300 this.canvasElCtx().drawImage(img, 0, 0);
45305 this.is_empty = false;
45308 selectImage: function()
45310 this.fileEl().dom.click();
45313 uploadImage: function(e)
45315 var reader = new FileReader();
45317 reader.onload = function(e){
45318 var img = new Image();
45319 img.onload = function(){
45321 this.canvasElCtx().drawImage(img, 0, 0);
45323 img.src = e.target.result;
45326 reader.readAsDataURL(e.target.files[0]);
45329 // Bezier Point Constructor
45330 Point: (function () {
45331 function Point(x, y, time) {
45334 this.time = time || Date.now();
45336 Point.prototype.distanceTo = function (start) {
45337 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45339 Point.prototype.equals = function (other) {
45340 return this.x === other.x && this.y === other.y && this.time === other.time;
45342 Point.prototype.velocityFrom = function (start) {
45343 return this.time !== start.time
45344 ? this.distanceTo(start) / (this.time - start.time)
45351 // Bezier Constructor
45352 Bezier: (function () {
45353 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45354 this.startPoint = startPoint;
45355 this.control2 = control2;
45356 this.control1 = control1;
45357 this.endPoint = endPoint;
45358 this.startWidth = startWidth;
45359 this.endWidth = endWidth;
45361 Bezier.fromPoints = function (points, widths, scope) {
45362 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45363 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45364 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45366 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45367 var dx1 = s1.x - s2.x;
45368 var dy1 = s1.y - s2.y;
45369 var dx2 = s2.x - s3.x;
45370 var dy2 = s2.y - s3.y;
45371 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45372 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45373 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45374 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45375 var dxm = m1.x - m2.x;
45376 var dym = m1.y - m2.y;
45377 var k = l2 / (l1 + l2);
45378 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45379 var tx = s2.x - cm.x;
45380 var ty = s2.y - cm.y;
45382 c1: new scope.Point(m1.x + tx, m1.y + ty),
45383 c2: new scope.Point(m2.x + tx, m2.y + ty)
45386 Bezier.prototype.length = function () {
45391 for (var i = 0; i <= steps; i += 1) {
45393 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45394 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45396 var xdiff = cx - px;
45397 var ydiff = cy - py;
45398 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45405 Bezier.prototype.point = function (t, start, c1, c2, end) {
45406 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45407 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45408 + (3.0 * c2 * (1.0 - t) * t * t)
45409 + (end * t * t * t);
45414 throttleStroke: function(fn, wait) {
45415 if (wait === void 0) { wait = 250; }
45417 var timeout = null;
45421 var later = function () {
45422 previous = Date.now();
45424 result = fn.apply(storedContext, storedArgs);
45426 storedContext = null;
45430 return function wrapper() {
45432 for (var _i = 0; _i < arguments.length; _i++) {
45433 args[_i] = arguments[_i];
45435 var now = Date.now();
45436 var remaining = wait - (now - previous);
45437 storedContext = this;
45439 if (remaining <= 0 || remaining > wait) {
45441 clearTimeout(timeout);
45445 result = fn.apply(storedContext, storedArgs);
45447 storedContext = null;
45451 else if (!timeout) {
45452 timeout = window.setTimeout(later, remaining);