2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
366 * Fetch the element to display the tooltip on.
367 * @return {Roo.Element} defaults to this.el
369 tooltipEl : function()
374 addxtype : function(tree,cntr)
378 cn = Roo.factory(tree);
379 //Roo.log(['addxtype', cn]);
381 cn.parentType = this.xtype; //??
382 cn.parentId = this.id;
384 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385 if (typeof(cn.container_method) == 'string') {
386 cntr = cn.container_method;
390 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
392 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
394 var build_from_html = Roo.XComponent.build_from_html;
396 var is_body = (tree.xtype == 'Body') ;
398 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
400 var self_cntr_el = Roo.get(this[cntr](false));
402 // do not try and build conditional elements
403 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
407 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409 return this.addxtypeChild(tree,cntr, is_body);
412 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
415 return this.addxtypeChild(Roo.apply({}, tree),cntr);
418 Roo.log('skipping render');
424 if (!build_from_html) {
428 // this i think handles overlaying multiple children of the same type
429 // with the sam eelement.. - which might be buggy..
431 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
441 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448 addxtypeChild : function (tree, cntr, is_body)
450 Roo.debug && Roo.log('addxtypeChild:' + cntr);
452 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
455 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456 (typeof(tree['flexy:foreach']) != 'undefined');
460 skip_children = false;
461 // render the element if it's not BODY.
464 // if parent was disabled, then do not try and create the children..
465 if(!this[cntr](true)){
470 cn = Roo.factory(tree);
472 cn.parentType = this.xtype; //??
473 cn.parentId = this.id;
475 var build_from_html = Roo.XComponent.build_from_html;
478 // does the container contain child eleemnts with 'xtype' attributes.
479 // that match this xtype..
480 // note - when we render we create these as well..
481 // so we should check to see if body has xtype set.
482 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
484 var self_cntr_el = Roo.get(this[cntr](false));
485 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
487 //Roo.log(Roo.XComponent.build_from_html);
488 //Roo.log("got echild:");
491 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492 // and are not displayed -this causes this to use up the wrong element when matching.
493 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
496 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503 //echild.dom.removeAttribute('xtype');
505 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506 Roo.debug && Roo.log(self_cntr_el);
507 Roo.debug && Roo.log(echild);
508 Roo.debug && Roo.log(cn);
514 // if object has flexy:if - then it may or may not be rendered.
515 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
516 // skip a flexy if element.
517 Roo.debug && Roo.log('skipping render');
518 Roo.debug && Roo.log(tree);
520 Roo.debug && Roo.log('skipping all children');
521 skip_children = true;
526 // actually if flexy:foreach is found, we really want to create
527 // multiple copies here...
529 //Roo.log(this[cntr]());
530 // some elements do not have render methods.. like the layouts...
532 if(this[cntr](true) === false){
537 cn.render && cn.render(this[cntr](true));
540 // then add the element..
547 if (typeof (tree.menu) != 'undefined') {
548 tree.menu.parentType = cn.xtype;
549 tree.menu.triggerEl = cn.el;
550 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
554 if (!tree.items || !tree.items.length) {
556 //Roo.log(["no children", this]);
561 var items = tree.items;
564 //Roo.log(items.length);
566 if (!skip_children) {
567 for(var i =0;i < items.length;i++) {
568 // Roo.log(['add child', items[i]]);
569 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575 //Roo.log("fire childrenrendered");
577 cn.fireEvent('childrenrendered', this);
583 * Set the element that will be used to show or hide
585 setVisibilityEl : function(el)
587 this.visibilityEl = el;
591 * Get the element that will be used to show or hide
593 getVisibilityEl : function()
595 if (typeof(this.visibilityEl) == 'object') {
596 return this.visibilityEl;
599 if (typeof(this.visibilityEl) == 'string') {
600 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607 * Show a component - removes 'hidden' class
611 if(!this.getVisibilityEl()){
615 this.getVisibilityEl().removeClass(['hidden','d-none']);
617 this.fireEvent('show', this);
622 * Hide a component - adds 'hidden' class
626 if(!this.getVisibilityEl()){
630 this.getVisibilityEl().addClass(['hidden','d-none']);
632 this.fireEvent('hide', this);
645 * @class Roo.bootstrap.Element
646 * @extends Roo.bootstrap.Component
647 * Bootstrap Element class
648 * @cfg {String} html contents of the element
649 * @cfg {String} tag tag of the element
650 * @cfg {String} cls class of the element
651 * @cfg {Boolean} preventDefault (true|false) default false
652 * @cfg {Boolean} clickable (true|false) default false
655 * Create a new Element
656 * @param {Object} config The config object
659 Roo.bootstrap.Element = function(config){
660 Roo.bootstrap.Element.superclass.constructor.call(this, config);
666 * When a element is chick
667 * @param {Roo.bootstrap.Element} this
668 * @param {Roo.EventObject} e
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
679 preventDefault: false,
682 getAutoCreate : function(){
686 // cls: this.cls, double assign in parent class Component.js :: onRender
693 initEvents: function()
695 Roo.bootstrap.Element.superclass.initEvents.call(this);
698 this.el.on('click', this.onClick, this);
703 onClick : function(e)
705 if(this.preventDefault){
709 this.fireEvent('click', this, e);
712 getValue : function()
714 return this.el.dom.innerHTML;
717 setValue : function(value)
719 this.el.dom.innerHTML = value;
734 * @class Roo.bootstrap.DropTarget
735 * @extends Roo.bootstrap.Element
736 * Bootstrap DropTarget class
738 * @cfg {string} name dropable name
741 * Create a new Dropable Area
742 * @param {Object} config The config object
745 Roo.bootstrap.DropTarget = function(config){
746 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
752 * When a element is chick
753 * @param {Roo.bootstrap.Element} this
754 * @param {Roo.EventObject} e
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
763 getAutoCreate : function(){
768 initEvents: function()
770 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
774 drop : this.dragDrop.createDelegate(this),
775 enter : this.dragEnter.createDelegate(this),
776 out : this.dragOut.createDelegate(this),
777 over : this.dragOver.createDelegate(this)
781 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
784 dragDrop : function(source,e,data)
786 // user has to decide how to impliment this.
789 //this.fireEvent('drop', this, source, e ,data);
793 dragEnter : function(n, dd, e, data)
795 // probably want to resize the element to match the dropped element..
797 this.originalSize = this.el.getSize();
798 this.el.setSize( n.el.getSize());
799 this.dropZone.DDM.refreshCache(this.name);
800 Roo.log([n, dd, e, data]);
803 dragOut : function(value)
805 // resize back to normal
807 this.el.setSize(this.originalSize);
808 this.dropZone.resetConstraints();
811 dragOver : function()
828 * @class Roo.bootstrap.Body
829 * @extends Roo.bootstrap.Component
830 * Bootstrap Body class
834 * @param {Object} config The config object
837 Roo.bootstrap.Body = function(config){
839 config = config || {};
841 Roo.bootstrap.Body.superclass.constructor.call(this, config);
842 this.el = Roo.get(config.el ? config.el : document.body );
843 if (this.cls && this.cls.length) {
844 Roo.get(document.body).addClass(this.cls);
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
850 is_body : true,// just to make sure it's constructed?
855 onRender : function(ct, position)
857 /* Roo.log("Roo.bootstrap.Body - onRender");
858 if (this.cls && this.cls.length) {
859 Roo.get(document.body).addClass(this.cls);
878 * @class Roo.bootstrap.ButtonGroup
879 * @extends Roo.bootstrap.Component
880 * Bootstrap ButtonGroup class
881 * @cfg {String} size lg | sm | xs (default empty normal)
882 * @cfg {String} align vertical | justified (default none)
883 * @cfg {String} direction up | down (default down)
884 * @cfg {Boolean} toolbar false | true
885 * @cfg {Boolean} btn true | false
890 * @param {Object} config The config object
893 Roo.bootstrap.ButtonGroup = function(config){
894 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
905 getAutoCreate : function(){
911 cfg.html = this.html || cfg.html;
922 if (['vertical','justified'].indexOf(this.align)!==-1) {
923 cfg.cls = 'btn-group-' + this.align;
925 if (this.align == 'justified') {
926 console.log(this.items);
930 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931 cfg.cls += ' btn-group-' + this.size;
934 if (this.direction == 'up') {
935 cfg.cls += ' dropup' ;
941 * Add a button to the group (similar to NavItem API.)
943 addItem : function(cfg)
945 var cn = new Roo.bootstrap.Button(cfg);
947 cn.parentId = this.id;
948 cn.onRender(this.el, null);
962 * @class Roo.bootstrap.Button
963 * @extends Roo.bootstrap.Component
964 * Bootstrap Button class
965 * @cfg {String} html The button content
966 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969 * @cfg {String} size (lg|sm|xs)
970 * @cfg {String} tag (a|input|submit)
971 * @cfg {String} href empty or href
972 * @cfg {Boolean} disabled default false;
973 * @cfg {Boolean} isClose default false;
974 * @cfg {String} glyphicon depricated - use fa
975 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976 * @cfg {String} badge text for badge
977 * @cfg {String} theme (default|glow)
978 * @cfg {Boolean} inverse dark themed version
979 * @cfg {Boolean} toggle is it a slidy toggle button
980 * @cfg {Boolean} pressed default null - if the button ahs active state
981 * @cfg {String} ontext text for on slidy toggle state
982 * @cfg {String} offtext text for off slidy toggle state
983 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
984 * @cfg {Boolean} removeClass remove the standard class..
985 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
986 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
989 * Create a new button
990 * @param {Object} config The config object
994 Roo.bootstrap.Button = function(config){
995 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1001 * When a button is pressed
1002 * @param {Roo.bootstrap.Button} btn
1003 * @param {Roo.EventObject} e
1008 * When a button is double clicked
1009 * @param {Roo.bootstrap.Button} btn
1010 * @param {Roo.EventObject} e
1015 * After the button has been toggles
1016 * @param {Roo.bootstrap.Button} btn
1017 * @param {Roo.EventObject} e
1018 * @param {boolean} pressed (also available as button.pressed)
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1045 preventDefault: true,
1054 getAutoCreate : function(){
1062 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064 this.tag = 'button';
1068 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1070 if (this.toggle == true) {
1073 cls: 'slider-frame roo-button',
1077 'data-on-text':'ON',
1078 'data-off-text':'OFF',
1079 cls: 'slider-button',
1084 // why are we validating the weights?
1085 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086 cfg.cls += ' ' + this.weight;
1093 cfg.cls += ' close';
1095 cfg["aria-hidden"] = true;
1097 cfg.html = "×";
1103 if (this.theme==='default') {
1104 cfg.cls = 'btn roo-button';
1106 //if (this.parentType != 'Navbar') {
1107 this.weight = this.weight.length ? this.weight : 'default';
1109 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1111 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113 cfg.cls += ' btn-' + outline + weight;
1114 if (this.weight == 'default') {
1116 cfg.cls += ' btn-' + this.weight;
1119 } else if (this.theme==='glow') {
1122 cfg.cls = 'btn-glow roo-button';
1124 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1126 cfg.cls += ' ' + this.weight;
1132 this.cls += ' inverse';
1136 if (this.active || this.pressed === true) {
1137 cfg.cls += ' active';
1140 if (this.disabled) {
1141 cfg.disabled = 'disabled';
1145 Roo.log('changing to ul' );
1147 this.glyphicon = 'caret';
1148 if (Roo.bootstrap.version == 4) {
1149 this.fa = 'caret-down';
1154 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1156 //gsRoo.log(this.parentType);
1157 if (this.parentType === 'Navbar' && !this.parent().bar) {
1158 Roo.log('changing to li?');
1167 href : this.href || '#'
1170 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1171 cfg.cls += ' dropdown';
1178 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1180 if (this.glyphicon) {
1181 cfg.html = ' ' + cfg.html;
1186 cls: 'glyphicon glyphicon-' + this.glyphicon
1191 cfg.html = ' ' + cfg.html;
1196 cls: 'fa fas fa-' + this.fa
1206 // cfg.cls='btn roo-button';
1210 var value = cfg.html;
1215 cls: 'glyphicon glyphicon-' + this.glyphicon,
1222 cls: 'fa fas fa-' + this.fa,
1227 var bw = this.badge_weight.length ? this.badge_weight :
1228 (this.weight.length ? this.weight : 'secondary');
1229 bw = bw == 'default' ? 'secondary' : bw;
1235 cls: 'badge badge-' + bw,
1244 cfg.cls += ' dropdown';
1245 cfg.html = typeof(cfg.html) != 'undefined' ?
1246 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1249 if (cfg.tag !== 'a' && this.href !== '') {
1250 throw "Tag must be a to set href.";
1251 } else if (this.href.length > 0) {
1252 cfg.href = this.href;
1255 if(this.removeClass){
1260 cfg.target = this.target;
1265 initEvents: function() {
1266 // Roo.log('init events?');
1267 // Roo.log(this.el.dom);
1270 if (typeof (this.menu) != 'undefined') {
1271 this.menu.parentType = this.xtype;
1272 this.menu.triggerEl = this.el;
1273 this.addxtype(Roo.apply({}, this.menu));
1277 if (this.el.hasClass('roo-button')) {
1278 this.el.on('click', this.onClick, this);
1279 this.el.on('dblclick', this.onDblClick, this);
1281 this.el.select('.roo-button').on('click', this.onClick, this);
1282 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1286 if(this.removeClass){
1287 this.el.on('click', this.onClick, this);
1290 if (this.group === true) {
1291 if (this.pressed === false || this.pressed === true) {
1294 this.pressed = false;
1295 this.setActive(this.pressed);
1300 this.el.enableDisplayMode();
1303 onClick : function(e)
1305 if (this.disabled) {
1309 Roo.log('button on click ');
1310 if(this.preventDefault){
1319 this.setActive(true);
1320 var pi = this.parent().items;
1321 for (var i = 0;i < pi.length;i++) {
1322 if (this == pi[i]) {
1325 if (pi[i].el.hasClass('roo-button')) {
1326 pi[i].setActive(false);
1329 this.fireEvent('click', this, e);
1333 if (this.pressed === true || this.pressed === false) {
1334 this.toggleActive(e);
1338 this.fireEvent('click', this, e);
1340 onDblClick: function(e)
1342 if (this.disabled) {
1345 if(this.preventDefault){
1348 this.fireEvent('dblclick', this, e);
1351 * Enables this button
1355 this.disabled = false;
1356 this.el.removeClass('disabled');
1360 * Disable this button
1362 disable : function()
1364 this.disabled = true;
1365 this.el.addClass('disabled');
1368 * sets the active state on/off,
1369 * @param {Boolean} state (optional) Force a particular state
1371 setActive : function(v) {
1373 this.el[v ? 'addClass' : 'removeClass']('active');
1377 * toggles the current active state
1379 toggleActive : function(e)
1381 this.setActive(!this.pressed); // this modifies pressed...
1382 this.fireEvent('toggle', this, e, this.pressed);
1385 * get the current active state
1386 * @return {boolean} true if it's active
1388 isActive : function()
1390 return this.el.hasClass('active');
1393 * set the text of the first selected button
1395 setText : function(str)
1397 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1400 * get the text of the first selected button
1402 getText : function()
1404 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1407 setWeight : function(str)
1409 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1412 var outline = this.outline ? 'outline-' : '';
1413 if (str == 'default') {
1414 this.el.addClass('btn-default btn-outline-secondary');
1417 this.el.addClass('btn-' + outline + str);
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1424 Roo.bootstrap.Button.weights = [
1444 * @class Roo.bootstrap.Column
1445 * @extends Roo.bootstrap.Component
1446 * Bootstrap Column class
1447 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1457 * @cfg {Boolean} hidden (true|false) hide the element
1458 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459 * @cfg {String} fa (ban|check|...) font awesome icon
1460 * @cfg {Number} fasize (1|2|....) font awsome size
1462 * @cfg {String} icon (info-sign|check|...) glyphicon name
1464 * @cfg {String} html content of column.
1467 * Create a new Column
1468 * @param {Object} config The config object
1471 Roo.bootstrap.Column = function(config){
1472 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1493 getAutoCreate : function(){
1494 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1502 var sizes = ['xs','sm','md','lg'];
1503 sizes.map(function(size ,ix){
1504 //Roo.log( size + ':' + settings[size]);
1506 if (settings[size+'off'] !== false) {
1507 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1510 if (settings[size] === false) {
1514 if (!settings[size]) { // 0 = hidden
1515 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1517 for (var i = ix; i > -1; i--) {
1518 cfg.cls += ' d-' + sizes[i] + '-none';
1524 cfg.cls += ' col-' + size + '-' + settings[size] + (
1525 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1531 cfg.cls += ' hidden';
1534 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535 cfg.cls +=' alert alert-' + this.alert;
1539 if (this.html.length) {
1540 cfg.html = this.html;
1544 if (this.fasize > 1) {
1545 fasize = ' fa-' + this.fasize + 'x';
1547 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1552 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1571 * @class Roo.bootstrap.Container
1572 * @extends Roo.bootstrap.Component
1573 * Bootstrap Container class
1574 * @cfg {Boolean} jumbotron is it a jumbotron element
1575 * @cfg {String} html content of element
1576 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1578 * @cfg {String} header content of header (for panel)
1579 * @cfg {String} footer content of footer (for panel)
1580 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581 * @cfg {String} tag (header|aside|section) type of HTML tag.
1582 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583 * @cfg {String} fa font awesome icon
1584 * @cfg {String} icon (info-sign|check|...) glyphicon name
1585 * @cfg {Boolean} hidden (true|false) hide the element
1586 * @cfg {Boolean} expandable (true|false) default false
1587 * @cfg {Boolean} expanded (true|false) default true
1588 * @cfg {String} rheader contet on the right of header
1589 * @cfg {Boolean} clickable (true|false) default false
1593 * Create a new Container
1594 * @param {Object} config The config object
1597 Roo.bootstrap.Container = function(config){
1598 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1604 * After the panel has been expand
1606 * @param {Roo.bootstrap.Container} this
1611 * After the panel has been collapsed
1613 * @param {Roo.bootstrap.Container} this
1618 * When a element is chick
1619 * @param {Roo.bootstrap.Container} this
1620 * @param {Roo.EventObject} e
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1644 getChildContainer : function() {
1650 if (this.panel.length) {
1651 return this.el.select('.panel-body',true).first();
1658 getAutoCreate : function(){
1661 tag : this.tag || 'div',
1665 if (this.jumbotron) {
1666 cfg.cls = 'jumbotron';
1671 // - this is applied by the parent..
1673 // cfg.cls = this.cls + '';
1676 if (this.sticky.length) {
1678 var bd = Roo.get(document.body);
1679 if (!bd.hasClass('bootstrap-sticky')) {
1680 bd.addClass('bootstrap-sticky');
1681 Roo.select('html',true).setStyle('height', '100%');
1684 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1688 if (this.well.length) {
1689 switch (this.well) {
1692 cfg.cls +=' well well-' +this.well;
1701 cfg.cls += ' hidden';
1705 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706 cfg.cls +=' alert alert-' + this.alert;
1711 if (this.panel.length) {
1712 cfg.cls += ' panel panel-' + this.panel;
1714 if (this.header.length) {
1718 if(this.expandable){
1720 cfg.cls = cfg.cls + ' expandable';
1724 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1732 cls : 'panel-title',
1733 html : (this.expandable ? ' ' : '') + this.header
1737 cls: 'panel-header-right',
1743 cls : 'panel-heading',
1744 style : this.expandable ? 'cursor: pointer' : '',
1752 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1757 if (this.footer.length) {
1759 cls : 'panel-footer',
1768 body.html = this.html || cfg.html;
1769 // prefix with the icons..
1771 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1774 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1779 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780 cfg.cls = 'container';
1786 initEvents: function()
1788 if(this.expandable){
1789 var headerEl = this.headerEl();
1792 headerEl.on('click', this.onToggleClick, this);
1797 this.el.on('click', this.onClick, this);
1802 onToggleClick : function()
1804 var headerEl = this.headerEl();
1820 if(this.fireEvent('expand', this)) {
1822 this.expanded = true;
1824 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1826 this.el.select('.panel-body',true).first().removeClass('hide');
1828 var toggleEl = this.toggleEl();
1834 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1839 collapse : function()
1841 if(this.fireEvent('collapse', this)) {
1843 this.expanded = false;
1845 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846 this.el.select('.panel-body',true).first().addClass('hide');
1848 var toggleEl = this.toggleEl();
1854 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1858 toggleEl : function()
1860 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1864 return this.el.select('.panel-heading .fa',true).first();
1867 headerEl : function()
1869 if(!this.el || !this.panel.length || !this.header.length){
1873 return this.el.select('.panel-heading',true).first()
1878 if(!this.el || !this.panel.length){
1882 return this.el.select('.panel-body',true).first()
1885 titleEl : function()
1887 if(!this.el || !this.panel.length || !this.header.length){
1891 return this.el.select('.panel-title',true).first();
1894 setTitle : function(v)
1896 var titleEl = this.titleEl();
1902 titleEl.dom.innerHTML = v;
1905 getTitle : function()
1908 var titleEl = this.titleEl();
1914 return titleEl.dom.innerHTML;
1917 setRightTitle : function(v)
1919 var t = this.el.select('.panel-header-right',true).first();
1925 t.dom.innerHTML = v;
1928 onClick : function(e)
1932 this.fireEvent('click', this, e);
1939 * This is BS4's Card element.. - similar to our containers probably..
1943 * @class Roo.bootstrap.Card
1944 * @extends Roo.bootstrap.Component
1945 * Bootstrap Card class
1948 * possible... may not be implemented..
1949 * @cfg {String} header_image src url of image.
1950 * @cfg {String|Object} header
1951 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1954 * @cfg {String} title
1955 * @cfg {String} subtitle
1956 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957 * @cfg {String} footer
1959 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1961 * @cfg {String} margin (0|1|2|3|4|5|auto)
1962 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1969 * @cfg {String} padding (0|1|2|3|4|5)
1970 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972 * @cfg {String} padding_left (0|1|2|3|4|5)
1973 * @cfg {String} padding_right (0|1|2|3|4|5)
1974 * @cfg {String} padding_x (0|1|2|3|4|5)
1975 * @cfg {String} padding_y (0|1|2|3|4|5)
1977 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1983 * @config {Boolean} dragable if this card can be dragged.
1984 * @config {String} drag_group group for drag
1985 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1986 * @config {String} drop_group group for drag
1988 * @config {Boolean} collapsable can the body be collapsed.
1989 * @config {Boolean} collapsed is the body collapsed when rendered...
1990 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991 * @config {Boolean} rotated is the body rotated when rendered...
1994 * Create a new Container
1995 * @param {Object} config The config object
1998 Roo.bootstrap.Card = function(config){
1999 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2005 * When a element a card is dropped
2006 * @param {Roo.bootstrap.Card} this
2009 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010 * @param {String} position 'above' or 'below'
2011 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2017 * When a element a card is rotate
2018 * @param {Roo.bootstrap.Element} this
2019 * @param {Roo.Element} n the node being dropped?
2020 * @param {Boolean} rotate status
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2033 margin: '', /// may be better in component?
2063 collapsable : false,
2072 childContainer : false,
2073 dropEl : false, /// the dom placeholde element that indicates drop location.
2074 containerEl: false, // body container
2075 bodyEl: false, // card-body
2076 headerContainerEl : false, //
2078 header_imageEl : false,
2080 layoutCls : function()
2084 Roo.log(this.margin_bottom.length);
2085 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2086 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2088 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2089 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2091 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2092 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2096 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2097 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2098 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2102 // more generic support?
2110 // Roo.log("Call onRender: " + this.xtype);
2111 /* We are looking at something like this.
2113 <img src="..." class="card-img-top" alt="...">
2114 <div class="card-body">
2115 <h5 class="card-title">Card title</h5>
2116 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2118 >> this bit is really the body...
2119 <div> << we will ad dthis in hopefully it will not break shit.
2121 ** card text does not actually have any styling...
2123 <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>
2126 <a href="#" class="card-link">Card link</a>
2129 <div class="card-footer">
2130 <small class="text-muted">Last updated 3 mins ago</small>
2134 getAutoCreate : function(){
2142 if (this.weight.length && this.weight != 'light') {
2143 cfg.cls += ' text-white';
2145 cfg.cls += ' text-dark'; // need as it's nested..
2147 if (this.weight.length) {
2148 cfg.cls += ' bg-' + this.weight;
2151 cfg.cls += ' ' + this.layoutCls();
2154 var hdr_ctr = false;
2155 if (this.header.length) {
2157 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2158 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2172 if (this.collapsable) {
2175 cls : 'd-block user-select-none',
2179 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2184 hdr.cn.push(hdr_ctr);
2189 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2194 if (this.header_image.length) {
2197 cls : 'card-img-top',
2198 src: this.header_image // escape?
2203 cls : 'card-img-top d-none'
2209 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2213 if (this.collapsable || this.rotateable) {
2216 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2223 if (this.title.length) {
2227 src: this.title // escape?
2231 if (this.subtitle.length) {
2235 src: this.subtitle // escape?
2241 cls : 'roo-card-body-ctr'
2244 if (this.html.length) {
2250 // fixme ? handle objects?
2252 if (this.footer.length) {
2255 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2260 cfg.cn.push({cls : 'card-footer d-none'});
2269 getCardHeader : function()
2271 var ret = this.el.select('.card-header',true).first();
2272 if (ret.hasClass('d-none')) {
2273 ret.removeClass('d-none');
2278 getCardFooter : function()
2280 var ret = this.el.select('.card-footer',true).first();
2281 if (ret.hasClass('d-none')) {
2282 ret.removeClass('d-none');
2287 getCardImageTop : function()
2289 var ret = this.header_imageEl;
2290 if (ret.hasClass('d-none')) {
2291 ret.removeClass('d-none');
2297 getChildContainer : function()
2303 return this.el.select('.roo-card-body-ctr',true).first();
2306 initEvents: function()
2308 this.bodyEl = this.el.select('.card-body',true).first();
2309 this.containerEl = this.getChildContainer();
2311 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2312 containerScroll: true,
2313 ddGroup: this.drag_group || 'default_card_drag_group'
2315 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2317 if (this.dropable) {
2318 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2319 containerScroll: true,
2320 ddGroup: this.drop_group || 'default_card_drag_group'
2322 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2323 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2324 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2325 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2326 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2329 if (this.collapsable) {
2330 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2332 if (this.rotateable) {
2333 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2335 this.collapsableEl = this.el.select('.roo-collapsable').first();
2337 this.footerEl = this.el.select('.card-footer').first();
2338 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2339 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2340 this.headerEl = this.el.select('.card-header',true).first();
2343 this.el.addClass('roo-card-rotated');
2344 this.fireEvent('rotate', this, true);
2346 this.header_imageEl = this.el.select('.card-img-top',true).first();
2347 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2350 getDragData : function(e)
2352 var target = this.getEl();
2354 //this.handleSelection(e);
2359 nodes: this.getEl(),
2364 dragData.ddel = target.dom ; // the div element
2365 Roo.log(target.getWidth( ));
2366 dragData.ddel.style.width = target.getWidth() + 'px';
2373 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2374 * whole Element becomes the target, and this causes the drop gesture to append.
2376 getTargetFromEvent : function(e, dragged_card_el)
2378 var target = e.getTarget();
2379 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2380 target = target.parentNode;
2391 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2392 // see if target is one of the 'cards'...
2395 //Roo.log(this.items.length);
2398 var last_card_n = 0;
2400 for (var i = 0;i< this.items.length;i++) {
2402 if (!this.items[i].el.hasClass('card')) {
2405 pos = this.getDropPoint(e, this.items[i].el.dom);
2407 cards_len = ret.cards.length;
2408 //Roo.log(this.items[i].el.dom.id);
2409 ret.cards.push(this.items[i]);
2411 if (ret.card_n < 0 && pos == 'above') {
2412 ret.position = cards_len > 0 ? 'below' : pos;
2413 ret.items_n = i > 0 ? i - 1 : 0;
2414 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2415 ret.card = ret.cards[ret.card_n];
2418 if (!ret.cards.length) {
2420 ret.position = 'below';
2424 // could not find a card.. stick it at the end..
2425 if (ret.card_n < 0) {
2426 ret.card_n = last_card_n;
2427 ret.card = ret.cards[last_card_n];
2428 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2429 ret.position = 'below';
2432 if (this.items[ret.items_n].el == dragged_card_el) {
2436 if (ret.position == 'below') {
2437 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2439 if (card_after && card_after.el == dragged_card_el) {
2446 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2448 if (card_before && card_before.el == dragged_card_el) {
2455 onNodeEnter : function(n, dd, e, data){
2458 onNodeOver : function(n, dd, e, data)
2461 var target_info = this.getTargetFromEvent(e,data.source.el);
2462 if (target_info === false) {
2463 this.dropPlaceHolder('hide');
2466 Roo.log(['getTargetFromEvent', target_info ]);
2469 this.dropPlaceHolder('show', target_info,data);
2473 onNodeOut : function(n, dd, e, data){
2474 this.dropPlaceHolder('hide');
2477 onNodeDrop : function(n, dd, e, data)
2480 // call drop - return false if
2482 // this could actually fail - if the Network drops..
2483 // we will ignore this at present..- client should probably reload
2484 // the whole set of cards if stuff like that fails.
2487 var info = this.getTargetFromEvent(e,data.source.el);
2488 if (info === false) {
2491 this.dropPlaceHolder('hide');
2497 this.acceptCard(data.source, info.position, info.card, info.items_n);
2501 firstChildCard : function()
2503 for (var i = 0;i< this.items.length;i++) {
2505 if (!this.items[i].el.hasClass('card')) {
2508 return this.items[i];
2510 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2515 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2517 acceptCard : function(move_card, position, next_to_card )
2519 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2523 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2525 move_card.parent().removeCard(move_card);
2528 var dom = move_card.el.dom;
2529 dom.style.width = ''; // clear with - which is set by drag.
2531 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2532 var cardel = next_to_card.el.dom;
2534 if (position == 'above' ) {
2535 cardel.parentNode.insertBefore(dom, cardel);
2536 } else if (cardel.nextSibling) {
2537 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2539 cardel.parentNode.append(dom);
2542 // card container???
2543 this.containerEl.dom.append(dom);
2546 //FIXME HANDLE card = true
2548 // add this to the correct place in items.
2550 // remove Card from items.
2553 if (this.items.length) {
2555 //Roo.log([info.items_n, info.position, this.items.length]);
2556 for (var i =0; i < this.items.length; i++) {
2557 if (i == to_items_n && position == 'above') {
2558 nitems.push(move_card);
2560 nitems.push(this.items[i]);
2561 if (i == to_items_n && position == 'below') {
2562 nitems.push(move_card);
2565 this.items = nitems;
2566 Roo.log(this.items);
2568 this.items.push(move_card);
2571 move_card.parentId = this.id;
2577 removeCard : function(c)
2579 this.items = this.items.filter(function(e) { return e != c });
2582 dom.parentNode.removeChild(dom);
2583 dom.style.width = ''; // clear with - which is set by drag.
2588 /** Decide whether to drop above or below a View node. */
2589 getDropPoint : function(e, n, dd)
2594 if (n == this.containerEl.dom) {
2597 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2598 var c = t + (b - t) / 2;
2599 var y = Roo.lib.Event.getPageY(e);
2606 onToggleCollapse : function(e)
2608 if (this.collapsed) {
2609 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2610 this.collapsableEl.addClass('show');
2611 this.collapsed = false;
2614 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2615 this.collapsableEl.removeClass('show');
2616 this.collapsed = true;
2621 onToggleRotate : function(e)
2623 this.collapsableEl.removeClass('show');
2624 this.footerEl.removeClass('d-none');
2625 this.el.removeClass('roo-card-rotated');
2626 this.el.removeClass('d-none');
2629 this.collapsableEl.addClass('show');
2630 this.rotated = false;
2631 this.fireEvent('rotate', this, this.rotated);
2634 this.el.addClass('roo-card-rotated');
2635 this.footerEl.addClass('d-none');
2636 this.el.select('.roo-collapsable').removeClass('show');
2638 this.rotated = true;
2639 this.fireEvent('rotate', this, this.rotated);
2643 dropPlaceHolder: function (action, info, data)
2645 if (this.dropEl === false) {
2646 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2650 this.dropEl.removeClass(['d-none', 'd-block']);
2651 if (action == 'hide') {
2653 this.dropEl.addClass('d-none');
2656 // FIXME - info.card == true!!!
2657 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2659 if (info.card !== true) {
2660 var cardel = info.card.el.dom;
2662 if (info.position == 'above') {
2663 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2664 } else if (cardel.nextSibling) {
2665 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2667 cardel.parentNode.append(this.dropEl.dom);
2670 // card container???
2671 this.containerEl.dom.append(this.dropEl.dom);
2674 this.dropEl.addClass('d-block roo-card-dropzone');
2676 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2683 setHeaderText: function(html)
2686 if (this.headerContainerEl) {
2687 this.headerContainerEl.dom.innerHTML = html;
2690 onHeaderImageLoad : function(ev, he)
2692 if (!this.header_image_fit_square) {
2696 var hw = he.naturalHeight / he.naturalWidth;
2699 //var w = he.dom.naturalWidth;
2702 Roo.get(he).setSize( ww * (1/hw), ww);
2703 Roo.get(he).setX( (ww - (ww * (1/hw))/ 2);
2714 * Card header - holder for the card header elements.
2719 * @class Roo.bootstrap.CardHeader
2720 * @extends Roo.bootstrap.Element
2721 * Bootstrap CardHeader class
2723 * Create a new Card Header - that you can embed children into
2724 * @param {Object} config The config object
2727 Roo.bootstrap.CardHeader = function(config){
2728 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2731 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2734 container_method : 'getCardHeader'
2747 * Card footer - holder for the card footer elements.
2752 * @class Roo.bootstrap.CardFooter
2753 * @extends Roo.bootstrap.Element
2754 * Bootstrap CardFooter class
2756 * Create a new Card Footer - that you can embed children into
2757 * @param {Object} config The config object
2760 Roo.bootstrap.CardFooter = function(config){
2761 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2764 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2767 container_method : 'getCardFooter'
2780 * Card header - holder for the card header elements.
2785 * @class Roo.bootstrap.CardImageTop
2786 * @extends Roo.bootstrap.Element
2787 * Bootstrap CardImageTop class
2789 * Create a new Card Image Top container
2790 * @param {Object} config The config object
2793 Roo.bootstrap.CardImageTop = function(config){
2794 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2797 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2800 container_method : 'getCardImageTop'
2818 * @class Roo.bootstrap.Img
2819 * @extends Roo.bootstrap.Component
2820 * Bootstrap Img class
2821 * @cfg {Boolean} imgResponsive false | true
2822 * @cfg {String} border rounded | circle | thumbnail
2823 * @cfg {String} src image source
2824 * @cfg {String} alt image alternative text
2825 * @cfg {String} href a tag href
2826 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2827 * @cfg {String} xsUrl xs image source
2828 * @cfg {String} smUrl sm image source
2829 * @cfg {String} mdUrl md image source
2830 * @cfg {String} lgUrl lg image source
2833 * Create a new Input
2834 * @param {Object} config The config object
2837 Roo.bootstrap.Img = function(config){
2838 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2844 * The img click event for the img.
2845 * @param {Roo.EventObject} e
2851 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2853 imgResponsive: true,
2863 getAutoCreate : function()
2865 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2866 return this.createSingleImg();
2871 cls: 'roo-image-responsive-group',
2876 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2878 if(!_this[size + 'Url']){
2884 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2885 html: _this.html || cfg.html,
2886 src: _this[size + 'Url']
2889 img.cls += ' roo-image-responsive-' + size;
2891 var s = ['xs', 'sm', 'md', 'lg'];
2893 s.splice(s.indexOf(size), 1);
2895 Roo.each(s, function(ss){
2896 img.cls += ' hidden-' + ss;
2899 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2900 cfg.cls += ' img-' + _this.border;
2904 cfg.alt = _this.alt;
2917 a.target = _this.target;
2921 cfg.cn.push((_this.href) ? a : img);
2928 createSingleImg : function()
2932 cls: (this.imgResponsive) ? 'img-responsive' : '',
2934 src : 'about:blank' // just incase src get's set to undefined?!?
2937 cfg.html = this.html || cfg.html;
2939 cfg.src = this.src || cfg.src;
2941 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2942 cfg.cls += ' img-' + this.border;
2959 a.target = this.target;
2964 return (this.href) ? a : cfg;
2967 initEvents: function()
2970 this.el.on('click', this.onClick, this);
2975 onClick : function(e)
2977 Roo.log('img onclick');
2978 this.fireEvent('click', this, e);
2981 * Sets the url of the image - used to update it
2982 * @param {String} url the url of the image
2985 setSrc : function(url)
2989 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2990 this.el.dom.src = url;
2994 this.el.select('img', true).first().dom.src = url;
3010 * @class Roo.bootstrap.Link
3011 * @extends Roo.bootstrap.Component
3012 * Bootstrap Link Class
3013 * @cfg {String} alt image alternative text
3014 * @cfg {String} href a tag href
3015 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3016 * @cfg {String} html the content of the link.
3017 * @cfg {String} anchor name for the anchor link
3018 * @cfg {String} fa - favicon
3020 * @cfg {Boolean} preventDefault (true | false) default false
3024 * Create a new Input
3025 * @param {Object} config The config object
3028 Roo.bootstrap.Link = function(config){
3029 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3035 * The img click event for the img.
3036 * @param {Roo.EventObject} e
3042 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3046 preventDefault: false,
3052 getAutoCreate : function()
3054 var html = this.html || '';
3056 if (this.fa !== false) {
3057 html = '<i class="fa fa-' + this.fa + '"></i>';
3062 // anchor's do not require html/href...
3063 if (this.anchor === false) {
3065 cfg.href = this.href || '#';
3067 cfg.name = this.anchor;
3068 if (this.html !== false || this.fa !== false) {
3071 if (this.href !== false) {
3072 cfg.href = this.href;
3076 if(this.alt !== false){
3081 if(this.target !== false) {
3082 cfg.target = this.target;
3088 initEvents: function() {
3090 if(!this.href || this.preventDefault){
3091 this.el.on('click', this.onClick, this);
3095 onClick : function(e)
3097 if(this.preventDefault){
3100 //Roo.log('img onclick');
3101 this.fireEvent('click', this, e);
3114 * @class Roo.bootstrap.Header
3115 * @extends Roo.bootstrap.Component
3116 * Bootstrap Header class
3117 * @cfg {String} html content of header
3118 * @cfg {Number} level (1|2|3|4|5|6) default 1
3121 * Create a new Header
3122 * @param {Object} config The config object
3126 Roo.bootstrap.Header = function(config){
3127 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3130 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3138 getAutoCreate : function(){
3143 tag: 'h' + (1 *this.level),
3144 html: this.html || ''
3156 * Ext JS Library 1.1.1
3157 * Copyright(c) 2006-2007, Ext JS, LLC.
3159 * Originally Released Under LGPL - original licence link has changed is not relivant.
3162 * <script type="text/javascript">
3166 * @class Roo.bootstrap.MenuMgr
3167 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3170 Roo.bootstrap.MenuMgr = function(){
3171 var menus, active, groups = {}, attached = false, lastShow = new Date();
3173 // private - called when first menu is created
3176 active = new Roo.util.MixedCollection();
3177 Roo.get(document).addKeyListener(27, function(){
3178 if(active.length > 0){
3186 if(active && active.length > 0){
3187 var c = active.clone();
3197 if(active.length < 1){
3198 Roo.get(document).un("mouseup", onMouseDown);
3206 var last = active.last();
3207 lastShow = new Date();
3210 Roo.get(document).on("mouseup", onMouseDown);
3215 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3216 m.parentMenu.activeChild = m;
3217 }else if(last && last.isVisible()){
3218 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3223 function onBeforeHide(m){
3225 m.activeChild.hide();
3227 if(m.autoHideTimer){
3228 clearTimeout(m.autoHideTimer);
3229 delete m.autoHideTimer;
3234 function onBeforeShow(m){
3235 var pm = m.parentMenu;
3236 if(!pm && !m.allowOtherMenus){
3238 }else if(pm && pm.activeChild && active != m){
3239 pm.activeChild.hide();
3243 // private this should really trigger on mouseup..
3244 function onMouseDown(e){
3245 Roo.log("on Mouse Up");
3247 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3248 Roo.log("MenuManager hideAll");
3257 function onBeforeCheck(mi, state){
3259 var g = groups[mi.group];
3260 for(var i = 0, l = g.length; i < l; i++){
3262 g[i].setChecked(false);
3271 * Hides all menus that are currently visible
3273 hideAll : function(){
3278 register : function(menu){
3282 menus[menu.id] = menu;
3283 menu.on("beforehide", onBeforeHide);
3284 menu.on("hide", onHide);
3285 menu.on("beforeshow", onBeforeShow);
3286 menu.on("show", onShow);
3288 if(g && menu.events["checkchange"]){
3292 groups[g].push(menu);
3293 menu.on("checkchange", onCheck);
3298 * Returns a {@link Roo.menu.Menu} object
3299 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3300 * be used to generate and return a new Menu instance.
3302 get : function(menu){
3303 if(typeof menu == "string"){ // menu id
3305 }else if(menu.events){ // menu instance
3308 /*else if(typeof menu.length == 'number'){ // array of menu items?
3309 return new Roo.bootstrap.Menu({items:menu});
3310 }else{ // otherwise, must be a config
3311 return new Roo.bootstrap.Menu(menu);
3318 unregister : function(menu){
3319 delete menus[menu.id];
3320 menu.un("beforehide", onBeforeHide);
3321 menu.un("hide", onHide);
3322 menu.un("beforeshow", onBeforeShow);
3323 menu.un("show", onShow);
3325 if(g && menu.events["checkchange"]){
3326 groups[g].remove(menu);
3327 menu.un("checkchange", onCheck);
3332 registerCheckable : function(menuItem){
3333 var g = menuItem.group;
3338 groups[g].push(menuItem);
3339 menuItem.on("beforecheckchange", onBeforeCheck);
3344 unregisterCheckable : function(menuItem){
3345 var g = menuItem.group;
3347 groups[g].remove(menuItem);
3348 menuItem.un("beforecheckchange", onBeforeCheck);
3360 * @class Roo.bootstrap.Menu
3361 * @extends Roo.bootstrap.Component
3362 * Bootstrap Menu class - container for MenuItems
3363 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3364 * @cfg {bool} hidden if the menu should be hidden when rendered.
3365 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3366 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3370 * @param {Object} config The config object
3374 Roo.bootstrap.Menu = function(config){
3375 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3376 if (this.registerMenu && this.type != 'treeview') {
3377 Roo.bootstrap.MenuMgr.register(this);
3384 * Fires before this menu is displayed (return false to block)
3385 * @param {Roo.menu.Menu} this
3390 * Fires before this menu is hidden (return false to block)
3391 * @param {Roo.menu.Menu} this
3396 * Fires after this menu is displayed
3397 * @param {Roo.menu.Menu} this
3402 * Fires after this menu is hidden
3403 * @param {Roo.menu.Menu} this
3408 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3409 * @param {Roo.menu.Menu} this
3410 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3411 * @param {Roo.EventObject} e
3416 * Fires when the mouse is hovering over this menu
3417 * @param {Roo.menu.Menu} this
3418 * @param {Roo.EventObject} e
3419 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3424 * Fires when the mouse exits this menu
3425 * @param {Roo.menu.Menu} this
3426 * @param {Roo.EventObject} e
3427 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3432 * Fires when a menu item contained in this menu is clicked
3433 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3434 * @param {Roo.EventObject} e
3438 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3441 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3445 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3448 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3450 registerMenu : true,
3452 menuItems :false, // stores the menu items..
3462 getChildContainer : function() {
3466 getAutoCreate : function(){
3468 //if (['right'].indexOf(this.align)!==-1) {
3469 // cfg.cn[1].cls += ' pull-right'
3475 cls : 'dropdown-menu' ,
3476 style : 'z-index:1000'
3480 if (this.type === 'submenu') {
3481 cfg.cls = 'submenu active';
3483 if (this.type === 'treeview') {
3484 cfg.cls = 'treeview-menu';
3489 initEvents : function() {
3491 // Roo.log("ADD event");
3492 // Roo.log(this.triggerEl.dom);
3494 this.triggerEl.on('click', this.onTriggerClick, this);
3496 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3499 if (this.triggerEl.hasClass('nav-item')) {
3500 // dropdown toggle on the 'a' in BS4?
3501 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3503 this.triggerEl.addClass('dropdown-toggle');
3506 this.el.on('touchstart' , this.onTouch, this);
3508 this.el.on('click' , this.onClick, this);
3510 this.el.on("mouseover", this.onMouseOver, this);
3511 this.el.on("mouseout", this.onMouseOut, this);
3515 findTargetItem : function(e)
3517 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3521 //Roo.log(t); Roo.log(t.id);
3523 //Roo.log(this.menuitems);
3524 return this.menuitems.get(t.id);
3526 //return this.items.get(t.menuItemId);
3532 onTouch : function(e)
3534 Roo.log("menu.onTouch");
3535 //e.stopEvent(); this make the user popdown broken
3539 onClick : function(e)
3541 Roo.log("menu.onClick");
3543 var t = this.findTargetItem(e);
3544 if(!t || t.isContainer){
3549 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3550 if(t == this.activeItem && t.shouldDeactivate(e)){
3551 this.activeItem.deactivate();
3552 delete this.activeItem;
3556 this.setActiveItem(t, true);
3564 Roo.log('pass click event');
3568 this.fireEvent("click", this, t, e);
3572 if(!t.href.length || t.href == '#'){
3573 (function() { _this.hide(); }).defer(100);
3578 onMouseOver : function(e){
3579 var t = this.findTargetItem(e);
3582 // if(t.canActivate && !t.disabled){
3583 // this.setActiveItem(t, true);
3587 this.fireEvent("mouseover", this, e, t);
3589 isVisible : function(){
3590 return !this.hidden;
3592 onMouseOut : function(e){
3593 var t = this.findTargetItem(e);
3596 // if(t == this.activeItem && t.shouldDeactivate(e)){
3597 // this.activeItem.deactivate();
3598 // delete this.activeItem;
3601 this.fireEvent("mouseout", this, e, t);
3606 * Displays this menu relative to another element
3607 * @param {String/HTMLElement/Roo.Element} element The element to align to
3608 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3609 * the element (defaults to this.defaultAlign)
3610 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3612 show : function(el, pos, parentMenu)
3614 if (false === this.fireEvent("beforeshow", this)) {
3615 Roo.log("show canceled");
3618 this.parentMenu = parentMenu;
3623 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3626 * Displays this menu at a specific xy position
3627 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3628 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3630 showAt : function(xy, parentMenu, /* private: */_e){
3631 this.parentMenu = parentMenu;
3636 this.fireEvent("beforeshow", this);
3637 //xy = this.el.adjustForConstraints(xy);
3641 this.hideMenuItems();
3642 this.hidden = false;
3643 this.triggerEl.addClass('open');
3644 this.el.addClass('show');
3646 // reassign x when hitting right
3647 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3648 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3651 // reassign y when hitting bottom
3652 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3653 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3656 // but the list may align on trigger left or trigger top... should it be a properity?
3658 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3663 this.fireEvent("show", this);
3669 this.doFocus.defer(50, this);
3673 doFocus : function(){
3675 this.focusEl.focus();
3680 * Hides this menu and optionally all parent menus
3681 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3683 hide : function(deep)
3685 if (false === this.fireEvent("beforehide", this)) {
3686 Roo.log("hide canceled");
3689 this.hideMenuItems();
3690 if(this.el && this.isVisible()){
3692 if(this.activeItem){
3693 this.activeItem.deactivate();
3694 this.activeItem = null;
3696 this.triggerEl.removeClass('open');;
3697 this.el.removeClass('show');
3699 this.fireEvent("hide", this);
3701 if(deep === true && this.parentMenu){
3702 this.parentMenu.hide(true);
3706 onTriggerClick : function(e)
3708 Roo.log('trigger click');
3710 var target = e.getTarget();
3712 Roo.log(target.nodeName.toLowerCase());
3714 if(target.nodeName.toLowerCase() === 'i'){
3720 onTriggerPress : function(e)
3722 Roo.log('trigger press');
3723 //Roo.log(e.getTarget());
3724 // Roo.log(this.triggerEl.dom);
3726 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3727 var pel = Roo.get(e.getTarget());
3728 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3729 Roo.log('is treeview or dropdown?');
3733 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3737 if (this.isVisible()) {
3742 this.show(this.triggerEl, '?', false);
3745 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3752 hideMenuItems : function()
3754 Roo.log("hide Menu Items");
3759 this.el.select('.open',true).each(function(aa) {
3761 aa.removeClass('open');
3765 addxtypeChild : function (tree, cntr) {
3766 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3768 this.menuitems.add(comp);
3780 this.getEl().dom.innerHTML = '';
3781 this.menuitems.clear();
3795 * @class Roo.bootstrap.MenuItem
3796 * @extends Roo.bootstrap.Component
3797 * Bootstrap MenuItem class
3798 * @cfg {String} html the menu label
3799 * @cfg {String} href the link
3800 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3801 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3802 * @cfg {Boolean} active used on sidebars to highlight active itesm
3803 * @cfg {String} fa favicon to show on left of menu item.
3804 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3808 * Create a new MenuItem
3809 * @param {Object} config The config object
3813 Roo.bootstrap.MenuItem = function(config){
3814 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3819 * The raw click event for the entire grid.
3820 * @param {Roo.bootstrap.MenuItem} this
3821 * @param {Roo.EventObject} e
3827 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3831 preventDefault: false,
3832 isContainer : false,
3836 getAutoCreate : function(){
3838 if(this.isContainer){
3841 cls: 'dropdown-menu-item '
3851 cls : 'dropdown-item',
3856 if (this.fa !== false) {
3859 cls : 'fa fa-' + this.fa
3868 cls: 'dropdown-menu-item',
3871 if (this.parent().type == 'treeview') {
3872 cfg.cls = 'treeview-menu';
3875 cfg.cls += ' active';
3880 anc.href = this.href || cfg.cn[0].href ;
3881 ctag.html = this.html || cfg.cn[0].html ;
3885 initEvents: function()
3887 if (this.parent().type == 'treeview') {
3888 this.el.select('a').on('click', this.onClick, this);
3892 this.menu.parentType = this.xtype;
3893 this.menu.triggerEl = this.el;
3894 this.menu = this.addxtype(Roo.apply({}, this.menu));
3898 onClick : function(e)
3900 Roo.log('item on click ');
3902 if(this.preventDefault){
3905 //this.parent().hideMenuItems();
3907 this.fireEvent('click', this, e);
3926 * @class Roo.bootstrap.MenuSeparator
3927 * @extends Roo.bootstrap.Component
3928 * Bootstrap MenuSeparator class
3931 * Create a new MenuItem
3932 * @param {Object} config The config object
3936 Roo.bootstrap.MenuSeparator = function(config){
3937 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3940 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3942 getAutoCreate : function(){
3961 * @class Roo.bootstrap.Modal
3962 * @extends Roo.bootstrap.Component
3963 * Bootstrap Modal class
3964 * @cfg {String} title Title of dialog
3965 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3966 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3967 * @cfg {Boolean} specificTitle default false
3968 * @cfg {Array} buttons Array of buttons or standard button set..
3969 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3970 * @cfg {Boolean} animate default true
3971 * @cfg {Boolean} allow_close default true
3972 * @cfg {Boolean} fitwindow default false
3973 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3974 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3975 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3976 * @cfg {String} size (sm|lg|xl) default empty
3977 * @cfg {Number} max_width set the max width of modal
3978 * @cfg {Boolean} editableTitle can the title be edited
3983 * Create a new Modal Dialog
3984 * @param {Object} config The config object
3987 Roo.bootstrap.Modal = function(config){
3988 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3993 * The raw btnclick event for the button
3994 * @param {Roo.EventObject} e
3999 * Fire when dialog resize
4000 * @param {Roo.bootstrap.Modal} this
4001 * @param {Roo.EventObject} e
4005 * @event titlechanged
4006 * Fire when the editable title has been changed
4007 * @param {Roo.bootstrap.Modal} this
4008 * @param {Roo.EventObject} value
4010 "titlechanged" : true
4013 this.buttons = this.buttons || [];
4016 this.tmpl = Roo.factory(this.tmpl);
4021 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4023 title : 'test dialog',
4033 specificTitle: false,
4035 buttonPosition: 'right',
4057 editableTitle : false,
4059 onRender : function(ct, position)
4061 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4064 var cfg = Roo.apply({}, this.getAutoCreate());
4067 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4069 //if (!cfg.name.length) {
4073 cfg.cls += ' ' + this.cls;
4076 cfg.style = this.style;
4078 this.el = Roo.get(document.body).createChild(cfg, position);
4080 //var type = this.el.dom.type;
4083 if(this.tabIndex !== undefined){
4084 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4087 this.dialogEl = this.el.select('.modal-dialog',true).first();
4088 this.bodyEl = this.el.select('.modal-body',true).first();
4089 this.closeEl = this.el.select('.modal-header .close', true).first();
4090 this.headerEl = this.el.select('.modal-header',true).first();
4091 this.titleEl = this.el.select('.modal-title',true).first();
4092 this.footerEl = this.el.select('.modal-footer',true).first();
4094 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4096 //this.el.addClass("x-dlg-modal");
4098 if (this.buttons.length) {
4099 Roo.each(this.buttons, function(bb) {
4100 var b = Roo.apply({}, bb);
4101 b.xns = b.xns || Roo.bootstrap;
4102 b.xtype = b.xtype || 'Button';
4103 if (typeof(b.listeners) == 'undefined') {
4104 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4107 var btn = Roo.factory(b);
4109 btn.render(this.getButtonContainer());
4113 // render the children.
4116 if(typeof(this.items) != 'undefined'){
4117 var items = this.items;
4120 for(var i =0;i < items.length;i++) {
4121 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4125 this.items = nitems;
4127 // where are these used - they used to be body/close/footer
4131 //this.el.addClass([this.fieldClass, this.cls]);
4135 getAutoCreate : function()
4137 // we will default to modal-body-overflow - might need to remove or make optional later.
4139 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4140 html : this.html || ''
4145 cls : 'modal-title',
4149 if(this.specificTitle){ // WTF is this?
4154 if (this.allow_close && Roo.bootstrap.version == 3) {
4164 if (this.editableTitle) {
4166 cls: 'form-control roo-editable-title d-none',
4172 if (this.allow_close && Roo.bootstrap.version == 4) {
4182 if(this.size.length){
4183 size = 'modal-' + this.size;
4186 var footer = Roo.bootstrap.version == 3 ?
4188 cls : 'modal-footer',
4192 cls: 'btn-' + this.buttonPosition
4197 { // BS4 uses mr-auto on left buttons....
4198 cls : 'modal-footer'
4209 cls: "modal-dialog " + size,
4212 cls : "modal-content",
4215 cls : 'modal-header',
4230 modal.cls += ' fade';
4236 getChildContainer : function() {
4241 getButtonContainer : function() {
4243 return Roo.bootstrap.version == 4 ?
4244 this.el.select('.modal-footer',true).first()
4245 : this.el.select('.modal-footer div',true).first();
4248 initEvents : function()
4250 if (this.allow_close) {
4251 this.closeEl.on('click', this.hide, this);
4253 Roo.EventManager.onWindowResize(this.resize, this, true);
4254 if (this.editableTitle) {
4255 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4256 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4257 this.headerEditEl.on('keyup', function(e) {
4258 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4259 this.toggleHeaderInput(false)
4262 this.headerEditEl.on('blur', function(e) {
4263 this.toggleHeaderInput(false)
4272 this.maskEl.setSize(
4273 Roo.lib.Dom.getViewWidth(true),
4274 Roo.lib.Dom.getViewHeight(true)
4277 if (this.fitwindow) {
4279 this.dialogEl.setStyle( { 'max-width' : '100%' });
4281 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4282 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4287 if(this.max_width !== 0) {
4289 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4292 this.setSize(w, this.height);
4296 if(this.max_height) {
4297 this.setSize(w,Math.min(
4299 Roo.lib.Dom.getViewportHeight(true) - 60
4305 if(!this.fit_content) {
4306 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4310 this.setSize(w, Math.min(
4312 this.headerEl.getHeight() +
4313 this.footerEl.getHeight() +
4314 this.getChildHeight(this.bodyEl.dom.childNodes),
4315 Roo.lib.Dom.getViewportHeight(true) - 60)
4321 setSize : function(w,h)
4332 if (!this.rendered) {
4335 this.toggleHeaderInput(false);
4336 //this.el.setStyle('display', 'block');
4337 this.el.removeClass('hideing');
4338 this.el.dom.style.display='block';
4340 Roo.get(document.body).addClass('modal-open');
4342 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4345 this.el.addClass('show');
4346 this.el.addClass('in');
4349 this.el.addClass('show');
4350 this.el.addClass('in');
4353 // not sure how we can show data in here..
4355 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4358 Roo.get(document.body).addClass("x-body-masked");
4360 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4361 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4362 this.maskEl.dom.style.display = 'block';
4363 this.maskEl.addClass('show');
4368 this.fireEvent('show', this);
4370 // set zindex here - otherwise it appears to be ignored...
4371 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4374 this.items.forEach( function(e) {
4375 e.layout ? e.layout() : false;
4383 if(this.fireEvent("beforehide", this) !== false){
4385 this.maskEl.removeClass('show');
4387 this.maskEl.dom.style.display = '';
4388 Roo.get(document.body).removeClass("x-body-masked");
4389 this.el.removeClass('in');
4390 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4392 if(this.animate){ // why
4393 this.el.addClass('hideing');
4394 this.el.removeClass('show');
4396 if (!this.el.hasClass('hideing')) {
4397 return; // it's been shown again...
4400 this.el.dom.style.display='';
4402 Roo.get(document.body).removeClass('modal-open');
4403 this.el.removeClass('hideing');
4407 this.el.removeClass('show');
4408 this.el.dom.style.display='';
4409 Roo.get(document.body).removeClass('modal-open');
4412 this.fireEvent('hide', this);
4415 isVisible : function()
4418 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4422 addButton : function(str, cb)
4426 var b = Roo.apply({}, { html : str } );
4427 b.xns = b.xns || Roo.bootstrap;
4428 b.xtype = b.xtype || 'Button';
4429 if (typeof(b.listeners) == 'undefined') {
4430 b.listeners = { click : cb.createDelegate(this) };
4433 var btn = Roo.factory(b);
4435 btn.render(this.getButtonContainer());
4441 setDefaultButton : function(btn)
4443 //this.el.select('.modal-footer').()
4446 resizeTo: function(w,h)
4448 this.dialogEl.setWidth(w);
4450 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4452 this.bodyEl.setHeight(h - diff);
4454 this.fireEvent('resize', this);
4457 setContentSize : function(w, h)
4461 onButtonClick: function(btn,e)
4464 this.fireEvent('btnclick', btn.name, e);
4467 * Set the title of the Dialog
4468 * @param {String} str new Title
4470 setTitle: function(str) {
4471 this.titleEl.dom.innerHTML = str;
4475 * Set the body of the Dialog
4476 * @param {String} str new Title
4478 setBody: function(str) {
4479 this.bodyEl.dom.innerHTML = str;
4482 * Set the body of the Dialog using the template
4483 * @param {Obj} data - apply this data to the template and replace the body contents.
4485 applyBody: function(obj)
4488 Roo.log("Error - using apply Body without a template");
4491 this.tmpl.overwrite(this.bodyEl, obj);
4494 getChildHeight : function(child_nodes)
4498 child_nodes.length == 0
4503 var child_height = 0;
4505 for(var i = 0; i < child_nodes.length; i++) {
4508 * for modal with tabs...
4509 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4511 var layout_childs = child_nodes[i].childNodes;
4513 for(var j = 0; j < layout_childs.length; j++) {
4515 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4517 var layout_body_childs = layout_childs[j].childNodes;
4519 for(var k = 0; k < layout_body_childs.length; k++) {
4521 if(layout_body_childs[k].classList.contains('navbar')) {
4522 child_height += layout_body_childs[k].offsetHeight;
4526 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4528 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4530 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4532 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4533 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4548 child_height += child_nodes[i].offsetHeight;
4549 // Roo.log(child_nodes[i].offsetHeight);
4552 return child_height;
4554 toggleHeaderInput : function(is_edit)
4556 if (!this.editableTitle) {
4557 return; // not editable.
4559 if (is_edit && this.is_header_editing) {
4560 return; // already editing..
4564 this.headerEditEl.dom.value = this.title;
4565 this.headerEditEl.removeClass('d-none');
4566 this.headerEditEl.dom.focus();
4567 this.titleEl.addClass('d-none');
4569 this.is_header_editing = true;
4572 // flip back to not editing.
4573 this.title = this.headerEditEl.dom.value;
4574 this.headerEditEl.addClass('d-none');
4575 this.titleEl.removeClass('d-none');
4576 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4577 this.is_header_editing = false;
4578 this.fireEvent('titlechanged', this, this.title);
4587 Roo.apply(Roo.bootstrap.Modal, {
4589 * Button config that displays a single OK button
4598 * Button config that displays Yes and No buttons
4614 * Button config that displays OK and Cancel buttons
4629 * Button config that displays Yes, No and Cancel buttons
4654 * messagebox - can be used as a replace
4658 * @class Roo.MessageBox
4659 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4663 Roo.Msg.alert('Status', 'Changes saved successfully.');
4665 // Prompt for user data:
4666 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4668 // process text value...
4672 // Show a dialog using config options:
4674 title:'Save Changes?',
4675 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4676 buttons: Roo.Msg.YESNOCANCEL,
4683 Roo.bootstrap.MessageBox = function(){
4684 var dlg, opt, mask, waitTimer;
4685 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4686 var buttons, activeTextEl, bwidth;
4690 var handleButton = function(button){
4692 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4696 var handleHide = function(){
4698 dlg.el.removeClass(opt.cls);
4701 // Roo.TaskMgr.stop(waitTimer);
4702 // waitTimer = null;
4707 var updateButtons = function(b){
4710 buttons["ok"].hide();
4711 buttons["cancel"].hide();
4712 buttons["yes"].hide();
4713 buttons["no"].hide();
4714 dlg.footerEl.hide();
4718 dlg.footerEl.show();
4719 for(var k in buttons){
4720 if(typeof buttons[k] != "function"){
4723 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4724 width += buttons[k].el.getWidth()+15;
4734 var handleEsc = function(d, k, e){
4735 if(opt && opt.closable !== false){
4745 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4746 * @return {Roo.BasicDialog} The BasicDialog element
4748 getDialog : function(){
4750 dlg = new Roo.bootstrap.Modal( {
4753 //constraintoviewport:false,
4755 //collapsible : false,
4760 //buttonAlign:"center",
4761 closeClick : function(){
4762 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4765 handleButton("cancel");
4770 dlg.on("hide", handleHide);
4772 //dlg.addKeyListener(27, handleEsc);
4774 this.buttons = buttons;
4775 var bt = this.buttonText;
4776 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4777 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4778 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4779 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4781 bodyEl = dlg.bodyEl.createChild({
4783 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4784 '<textarea class="roo-mb-textarea"></textarea>' +
4785 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4787 msgEl = bodyEl.dom.firstChild;
4788 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4789 textboxEl.enableDisplayMode();
4790 textboxEl.addKeyListener([10,13], function(){
4791 if(dlg.isVisible() && opt && opt.buttons){
4794 }else if(opt.buttons.yes){
4795 handleButton("yes");
4799 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4800 textareaEl.enableDisplayMode();
4801 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4802 progressEl.enableDisplayMode();
4804 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4805 var pf = progressEl.dom.firstChild;
4807 pp = Roo.get(pf.firstChild);
4808 pp.setHeight(pf.offsetHeight);
4816 * Updates the message box body text
4817 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4818 * the XHTML-compliant non-breaking space character '&#160;')
4819 * @return {Roo.MessageBox} This message box
4821 updateText : function(text)
4823 if(!dlg.isVisible() && !opt.width){
4824 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4825 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4827 msgEl.innerHTML = text || ' ';
4829 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4830 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4832 Math.min(opt.width || cw , this.maxWidth),
4833 Math.max(opt.minWidth || this.minWidth, bwidth)
4836 activeTextEl.setWidth(w);
4838 if(dlg.isVisible()){
4839 dlg.fixedcenter = false;
4841 // to big, make it scroll. = But as usual stupid IE does not support
4844 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4845 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4846 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4848 bodyEl.dom.style.height = '';
4849 bodyEl.dom.style.overflowY = '';
4852 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4854 bodyEl.dom.style.overflowX = '';
4857 dlg.setContentSize(w, bodyEl.getHeight());
4858 if(dlg.isVisible()){
4859 dlg.fixedcenter = true;
4865 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4866 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4867 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4868 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4869 * @return {Roo.MessageBox} This message box
4871 updateProgress : function(value, text){
4873 this.updateText(text);
4876 if (pp) { // weird bug on my firefox - for some reason this is not defined
4877 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4878 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4884 * Returns true if the message box is currently displayed
4885 * @return {Boolean} True if the message box is visible, else false
4887 isVisible : function(){
4888 return dlg && dlg.isVisible();
4892 * Hides the message box if it is displayed
4895 if(this.isVisible()){
4901 * Displays a new message box, or reinitializes an existing message box, based on the config options
4902 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4903 * The following config object properties are supported:
4905 Property Type Description
4906 ---------- --------------- ------------------------------------------------------------------------------------
4907 animEl String/Element An id or Element from which the message box should animate as it opens and
4908 closes (defaults to undefined)
4909 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4910 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4911 closable Boolean False to hide the top-right close button (defaults to true). Note that
4912 progress and wait dialogs will ignore this property and always hide the
4913 close button as they can only be closed programmatically.
4914 cls String A custom CSS class to apply to the message box element
4915 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4916 displayed (defaults to 75)
4917 fn Function A callback function to execute after closing the dialog. The arguments to the
4918 function will be btn (the name of the button that was clicked, if applicable,
4919 e.g. "ok"), and text (the value of the active text field, if applicable).
4920 Progress and wait dialogs will ignore this option since they do not respond to
4921 user actions and can only be closed programmatically, so any required function
4922 should be called by the same code after it closes the dialog.
4923 icon String A CSS class that provides a background image to be used as an icon for
4924 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4925 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4926 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4927 modal Boolean False to allow user interaction with the page while the message box is
4928 displayed (defaults to true)
4929 msg String A string that will replace the existing message box body text (defaults
4930 to the XHTML-compliant non-breaking space character ' ')
4931 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4932 progress Boolean True to display a progress bar (defaults to false)
4933 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4934 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4935 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4936 title String The title text
4937 value String The string value to set into the active textbox element if displayed
4938 wait Boolean True to display a progress bar (defaults to false)
4939 width Number The width of the dialog in pixels
4946 msg: 'Please enter your address:',
4948 buttons: Roo.MessageBox.OKCANCEL,
4951 animEl: 'addAddressBtn'
4954 * @param {Object} config Configuration options
4955 * @return {Roo.MessageBox} This message box
4957 show : function(options)
4960 // this causes nightmares if you show one dialog after another
4961 // especially on callbacks..
4963 if(this.isVisible()){
4966 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4967 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4968 Roo.log("New Dialog Message:" + options.msg )
4969 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4970 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4973 var d = this.getDialog();
4975 d.setTitle(opt.title || " ");
4976 d.closeEl.setDisplayed(opt.closable !== false);
4977 activeTextEl = textboxEl;
4978 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4983 textareaEl.setHeight(typeof opt.multiline == "number" ?
4984 opt.multiline : this.defaultTextHeight);
4985 activeTextEl = textareaEl;
4994 progressEl.setDisplayed(opt.progress === true);
4996 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4998 this.updateProgress(0);
4999 activeTextEl.dom.value = opt.value || "";
5001 dlg.setDefaultButton(activeTextEl);
5003 var bs = opt.buttons;
5007 }else if(bs && bs.yes){
5008 db = buttons["yes"];
5010 dlg.setDefaultButton(db);
5012 bwidth = updateButtons(opt.buttons);
5013 this.updateText(opt.msg);
5015 d.el.addClass(opt.cls);
5017 d.proxyDrag = opt.proxyDrag === true;
5018 d.modal = opt.modal !== false;
5019 d.mask = opt.modal !== false ? mask : false;
5021 // force it to the end of the z-index stack so it gets a cursor in FF
5022 document.body.appendChild(dlg.el.dom);
5023 d.animateTarget = null;
5024 d.show(options.animEl);
5030 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5031 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5032 * and closing the message box when the process is complete.
5033 * @param {String} title The title bar text
5034 * @param {String} msg The message box body text
5035 * @return {Roo.MessageBox} This message box
5037 progress : function(title, msg){
5044 minWidth: this.minProgressWidth,
5051 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5052 * If a callback function is passed it will be called after the user clicks the button, and the
5053 * id of the button that was clicked will be passed as the only parameter to the callback
5054 * (could also be the top-right close button).
5055 * @param {String} title The title bar text
5056 * @param {String} msg The message box body text
5057 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5058 * @param {Object} scope (optional) The scope of the callback function
5059 * @return {Roo.MessageBox} This message box
5061 alert : function(title, msg, fn, scope)
5076 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5077 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5078 * You are responsible for closing the message box when the process is complete.
5079 * @param {String} msg The message box body text
5080 * @param {String} title (optional) The title bar text
5081 * @return {Roo.MessageBox} This message box
5083 wait : function(msg, title){
5094 waitTimer = Roo.TaskMgr.start({
5096 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5104 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5105 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5106 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5107 * @param {String} title The title bar text
5108 * @param {String} msg The message box body text
5109 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5110 * @param {Object} scope (optional) The scope of the callback function
5111 * @return {Roo.MessageBox} This message box
5113 confirm : function(title, msg, fn, scope){
5117 buttons: this.YESNO,
5126 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5127 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5128 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5129 * (could also be the top-right close button) and the text that was entered will be passed as the two
5130 * parameters to the callback.
5131 * @param {String} title The title bar text
5132 * @param {String} msg The message box body text
5133 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5134 * @param {Object} scope (optional) The scope of the callback function
5135 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5136 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5137 * @return {Roo.MessageBox} This message box
5139 prompt : function(title, msg, fn, scope, multiline){
5143 buttons: this.OKCANCEL,
5148 multiline: multiline,
5155 * Button config that displays a single OK button
5160 * Button config that displays Yes and No buttons
5163 YESNO : {yes:true, no:true},
5165 * Button config that displays OK and Cancel buttons
5168 OKCANCEL : {ok:true, cancel:true},
5170 * Button config that displays Yes, No and Cancel buttons
5173 YESNOCANCEL : {yes:true, no:true, cancel:true},
5176 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5179 defaultTextHeight : 75,
5181 * The maximum width in pixels of the message box (defaults to 600)
5186 * The minimum width in pixels of the message box (defaults to 100)
5191 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5192 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5195 minProgressWidth : 250,
5197 * An object containing the default button text strings that can be overriden for localized language support.
5198 * Supported properties are: ok, cancel, yes and no.
5199 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5212 * Shorthand for {@link Roo.MessageBox}
5214 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5215 Roo.Msg = Roo.Msg || Roo.MessageBox;
5224 * @class Roo.bootstrap.Navbar
5225 * @extends Roo.bootstrap.Component
5226 * Bootstrap Navbar class
5229 * Create a new Navbar
5230 * @param {Object} config The config object
5234 Roo.bootstrap.Navbar = function(config){
5235 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5239 * @event beforetoggle
5240 * Fire before toggle the menu
5241 * @param {Roo.EventObject} e
5243 "beforetoggle" : true
5247 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5256 getAutoCreate : function(){
5259 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5263 initEvents :function ()
5265 //Roo.log(this.el.select('.navbar-toggle',true));
5266 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5273 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5275 var size = this.el.getSize();
5276 this.maskEl.setSize(size.width, size.height);
5277 this.maskEl.enableDisplayMode("block");
5286 getChildContainer : function()
5288 if (this.el && this.el.select('.collapse').getCount()) {
5289 return this.el.select('.collapse',true).first();
5304 onToggle : function()
5307 if(this.fireEvent('beforetoggle', this) === false){
5310 var ce = this.el.select('.navbar-collapse',true).first();
5312 if (!ce.hasClass('show')) {
5322 * Expand the navbar pulldown
5324 expand : function ()
5327 var ce = this.el.select('.navbar-collapse',true).first();
5328 if (ce.hasClass('collapsing')) {
5331 ce.dom.style.height = '';
5333 ce.addClass('in'); // old...
5334 ce.removeClass('collapse');
5335 ce.addClass('show');
5336 var h = ce.getHeight();
5338 ce.removeClass('show');
5339 // at this point we should be able to see it..
5340 ce.addClass('collapsing');
5342 ce.setHeight(0); // resize it ...
5343 ce.on('transitionend', function() {
5344 //Roo.log('done transition');
5345 ce.removeClass('collapsing');
5346 ce.addClass('show');
5347 ce.removeClass('collapse');
5349 ce.dom.style.height = '';
5350 }, this, { single: true} );
5352 ce.dom.scrollTop = 0;
5355 * Collapse the navbar pulldown
5357 collapse : function()
5359 var ce = this.el.select('.navbar-collapse',true).first();
5361 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5362 // it's collapsed or collapsing..
5365 ce.removeClass('in'); // old...
5366 ce.setHeight(ce.getHeight());
5367 ce.removeClass('show');
5368 ce.addClass('collapsing');
5370 ce.on('transitionend', function() {
5371 ce.dom.style.height = '';
5372 ce.removeClass('collapsing');
5373 ce.addClass('collapse');
5374 }, this, { single: true} );
5394 * @class Roo.bootstrap.NavSimplebar
5395 * @extends Roo.bootstrap.Navbar
5396 * Bootstrap Sidebar class
5398 * @cfg {Boolean} inverse is inverted color
5400 * @cfg {String} type (nav | pills | tabs)
5401 * @cfg {Boolean} arrangement stacked | justified
5402 * @cfg {String} align (left | right) alignment
5404 * @cfg {Boolean} main (true|false) main nav bar? default false
5405 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5407 * @cfg {String} tag (header|footer|nav|div) default is nav
5409 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5413 * Create a new Sidebar
5414 * @param {Object} config The config object
5418 Roo.bootstrap.NavSimplebar = function(config){
5419 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5422 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5438 getAutoCreate : function(){
5442 tag : this.tag || 'div',
5443 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5445 if (['light','white'].indexOf(this.weight) > -1) {
5446 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5448 cfg.cls += ' bg-' + this.weight;
5451 cfg.cls += ' navbar-inverse';
5455 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5457 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5466 cls: 'nav nav-' + this.xtype,
5472 this.type = this.type || 'nav';
5473 if (['tabs','pills'].indexOf(this.type) != -1) {
5474 cfg.cn[0].cls += ' nav-' + this.type
5478 if (this.type!=='nav') {
5479 Roo.log('nav type must be nav/tabs/pills')
5481 cfg.cn[0].cls += ' navbar-nav'
5487 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5488 cfg.cn[0].cls += ' nav-' + this.arrangement;
5492 if (this.align === 'right') {
5493 cfg.cn[0].cls += ' navbar-right';
5518 * navbar-expand-md fixed-top
5522 * @class Roo.bootstrap.NavHeaderbar
5523 * @extends Roo.bootstrap.NavSimplebar
5524 * Bootstrap Sidebar class
5526 * @cfg {String} brand what is brand
5527 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5528 * @cfg {String} brand_href href of the brand
5529 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5530 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5531 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5532 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5535 * Create a new Sidebar
5536 * @param {Object} config The config object
5540 Roo.bootstrap.NavHeaderbar = function(config){
5541 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5545 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5552 desktopCenter : false,
5555 getAutoCreate : function(){
5558 tag: this.nav || 'nav',
5559 cls: 'navbar navbar-expand-md',
5565 if (this.desktopCenter) {
5566 cn.push({cls : 'container', cn : []});
5574 cls: 'navbar-toggle navbar-toggler',
5575 'data-toggle': 'collapse',
5580 html: 'Toggle navigation'
5584 cls: 'icon-bar navbar-toggler-icon'
5597 cn.push( Roo.bootstrap.version == 4 ? btn : {
5599 cls: 'navbar-header',
5608 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5612 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5614 if (['light','white'].indexOf(this.weight) > -1) {
5615 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5617 cfg.cls += ' bg-' + this.weight;
5620 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5621 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5623 // tag can override this..
5625 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5628 if (this.brand !== '') {
5629 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5630 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5632 href: this.brand_href ? this.brand_href : '#',
5633 cls: 'navbar-brand',
5641 cfg.cls += ' main-nav';
5649 getHeaderChildContainer : function()
5651 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5652 return this.el.select('.navbar-header',true).first();
5655 return this.getChildContainer();
5658 getChildContainer : function()
5661 return this.el.select('.roo-navbar-collapse',true).first();
5666 initEvents : function()
5668 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5670 if (this.autohide) {
5675 Roo.get(document).on('scroll',function(e) {
5676 var ns = Roo.get(document).getScroll().top;
5677 var os = prevScroll;
5681 ft.removeClass('slideDown');
5682 ft.addClass('slideUp');
5685 ft.removeClass('slideUp');
5686 ft.addClass('slideDown');
5707 * @class Roo.bootstrap.NavSidebar
5708 * @extends Roo.bootstrap.Navbar
5709 * Bootstrap Sidebar class
5712 * Create a new Sidebar
5713 * @param {Object} config The config object
5717 Roo.bootstrap.NavSidebar = function(config){
5718 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5721 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5723 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5725 getAutoCreate : function(){
5730 cls: 'sidebar sidebar-nav'
5752 * @class Roo.bootstrap.NavGroup
5753 * @extends Roo.bootstrap.Component
5754 * Bootstrap NavGroup class
5755 * @cfg {String} align (left|right)
5756 * @cfg {Boolean} inverse
5757 * @cfg {String} type (nav|pills|tab) default nav
5758 * @cfg {String} navId - reference Id for navbar.
5759 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5762 * Create a new nav group
5763 * @param {Object} config The config object
5766 Roo.bootstrap.NavGroup = function(config){
5767 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5770 Roo.bootstrap.NavGroup.register(this);
5774 * Fires when the active item changes
5775 * @param {Roo.bootstrap.NavGroup} this
5776 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5777 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5784 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5796 getAutoCreate : function()
5798 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5804 if (Roo.bootstrap.version == 4) {
5805 if (['tabs','pills'].indexOf(this.type) != -1) {
5806 cfg.cls += ' nav-' + this.type;
5808 // trying to remove so header bar can right align top?
5809 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5810 // do not use on header bar...
5811 cfg.cls += ' navbar-nav';
5816 if (['tabs','pills'].indexOf(this.type) != -1) {
5817 cfg.cls += ' nav-' + this.type
5819 if (this.type !== 'nav') {
5820 Roo.log('nav type must be nav/tabs/pills')
5822 cfg.cls += ' navbar-nav'
5826 if (this.parent() && this.parent().sidebar) {
5829 cls: 'dashboard-menu sidebar-menu'
5835 if (this.form === true) {
5838 cls: 'navbar-form form-inline'
5840 //nav navbar-right ml-md-auto
5841 if (this.align === 'right') {
5842 cfg.cls += ' navbar-right ml-md-auto';
5844 cfg.cls += ' navbar-left';
5848 if (this.align === 'right') {
5849 cfg.cls += ' navbar-right ml-md-auto';
5851 cfg.cls += ' mr-auto';
5855 cfg.cls += ' navbar-inverse';
5863 * sets the active Navigation item
5864 * @param {Roo.bootstrap.NavItem} the new current navitem
5866 setActiveItem : function(item)
5869 Roo.each(this.navItems, function(v){
5874 v.setActive(false, true);
5881 item.setActive(true, true);
5882 this.fireEvent('changed', this, item, prev);
5887 * gets the active Navigation item
5888 * @return {Roo.bootstrap.NavItem} the current navitem
5890 getActive : function()
5894 Roo.each(this.navItems, function(v){
5905 indexOfNav : function()
5909 Roo.each(this.navItems, function(v,i){
5920 * adds a Navigation item
5921 * @param {Roo.bootstrap.NavItem} the navitem to add
5923 addItem : function(cfg)
5925 if (this.form && Roo.bootstrap.version == 4) {
5928 var cn = new Roo.bootstrap.NavItem(cfg);
5930 cn.parentId = this.id;
5931 cn.onRender(this.el, null);
5935 * register a Navigation item
5936 * @param {Roo.bootstrap.NavItem} the navitem to add
5938 register : function(item)
5940 this.navItems.push( item);
5941 item.navId = this.navId;
5946 * clear all the Navigation item
5949 clearAll : function()
5952 this.el.dom.innerHTML = '';
5955 getNavItem: function(tabId)
5958 Roo.each(this.navItems, function(e) {
5959 if (e.tabId == tabId) {
5969 setActiveNext : function()
5971 var i = this.indexOfNav(this.getActive());
5972 if (i > this.navItems.length) {
5975 this.setActiveItem(this.navItems[i+1]);
5977 setActivePrev : function()
5979 var i = this.indexOfNav(this.getActive());
5983 this.setActiveItem(this.navItems[i-1]);
5985 clearWasActive : function(except) {
5986 Roo.each(this.navItems, function(e) {
5987 if (e.tabId != except.tabId && e.was_active) {
5988 e.was_active = false;
5995 getWasActive : function ()
5998 Roo.each(this.navItems, function(e) {
6013 Roo.apply(Roo.bootstrap.NavGroup, {
6017 * register a Navigation Group
6018 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6020 register : function(navgrp)
6022 this.groups[navgrp.navId] = navgrp;
6026 * fetch a Navigation Group based on the navigation ID
6027 * @param {string} the navgroup to add
6028 * @returns {Roo.bootstrap.NavGroup} the navgroup
6030 get: function(navId) {
6031 if (typeof(this.groups[navId]) == 'undefined') {
6033 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6035 return this.groups[navId] ;
6050 * @class Roo.bootstrap.NavItem
6051 * @extends Roo.bootstrap.Component
6052 * Bootstrap Navbar.NavItem class
6053 * @cfg {String} href link to
6054 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6055 * @cfg {Boolean} button_outline show and outlined button
6056 * @cfg {String} html content of button
6057 * @cfg {String} badge text inside badge
6058 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6059 * @cfg {String} glyphicon DEPRICATED - use fa
6060 * @cfg {String} icon DEPRICATED - use fa
6061 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6062 * @cfg {Boolean} active Is item active
6063 * @cfg {Boolean} disabled Is item disabled
6064 * @cfg {String} linkcls Link Class
6065 * @cfg {Boolean} preventDefault (true | false) default false
6066 * @cfg {String} tabId the tab that this item activates.
6067 * @cfg {String} tagtype (a|span) render as a href or span?
6068 * @cfg {Boolean} animateRef (true|false) link to element default false
6071 * Create a new Navbar Item
6072 * @param {Object} config The config object
6074 Roo.bootstrap.NavItem = function(config){
6075 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6080 * The raw click event for the entire grid.
6081 * @param {Roo.EventObject} e
6086 * Fires when the active item active state changes
6087 * @param {Roo.bootstrap.NavItem} this
6088 * @param {boolean} state the new state
6094 * Fires when scroll to element
6095 * @param {Roo.bootstrap.NavItem} this
6096 * @param {Object} options
6097 * @param {Roo.EventObject} e
6105 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6114 preventDefault : false,
6122 button_outline : false,
6126 getAutoCreate : function(){
6133 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6136 cfg.cls += ' active' ;
6138 if (this.disabled) {
6139 cfg.cls += ' disabled';
6143 if (this.button_weight.length) {
6144 cfg.tag = this.href ? 'a' : 'button';
6145 cfg.html = this.html || '';
6146 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6148 cfg.href = this.href;
6151 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6154 // menu .. should add dropdown-menu class - so no need for carat..
6156 if (this.badge !== '') {
6158 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6163 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6167 href : this.href || "#",
6168 html: this.html || ''
6171 if (this.tagtype == 'a') {
6172 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6176 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6179 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6181 if(this.glyphicon) {
6182 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6187 cfg.cn[0].html += " <span class='caret'></span>";
6191 if (this.badge !== '') {
6193 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6201 onRender : function(ct, position)
6203 // Roo.log("Call onRender: " + this.xtype);
6204 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6208 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6209 this.navLink = this.el.select('.nav-link',true).first();
6214 initEvents: function()
6216 if (typeof (this.menu) != 'undefined') {
6217 this.menu.parentType = this.xtype;
6218 this.menu.triggerEl = this.el;
6219 this.menu = this.addxtype(Roo.apply({}, this.menu));
6222 this.el.on('click', this.onClick, this);
6224 //if(this.tagtype == 'span'){
6225 // this.el.select('span',true).on('click', this.onClick, this);
6228 // at this point parent should be available..
6229 this.parent().register(this);
6232 onClick : function(e)
6234 if (e.getTarget('.dropdown-menu-item')) {
6235 // did you click on a menu itemm.... - then don't trigger onclick..
6240 this.preventDefault ||
6243 Roo.log("NavItem - prevent Default?");
6247 if (this.disabled) {
6251 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6252 if (tg && tg.transition) {
6253 Roo.log("waiting for the transitionend");
6259 //Roo.log("fire event clicked");
6260 if(this.fireEvent('click', this, e) === false){
6264 if(this.tagtype == 'span'){
6268 //Roo.log(this.href);
6269 var ael = this.el.select('a',true).first();
6272 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6273 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6274 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6275 return; // ignore... - it's a 'hash' to another page.
6277 Roo.log("NavItem - prevent Default?");
6279 this.scrollToElement(e);
6283 var p = this.parent();
6285 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6286 if (typeof(p.setActiveItem) !== 'undefined') {
6287 p.setActiveItem(this);
6291 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6292 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6293 // remove the collapsed menu expand...
6294 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6298 isActive: function () {
6301 setActive : function(state, fire, is_was_active)
6303 if (this.active && !state && this.navId) {
6304 this.was_active = true;
6305 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6307 nv.clearWasActive(this);
6311 this.active = state;
6314 this.el.removeClass('active');
6315 this.navLink ? this.navLink.removeClass('active') : false;
6316 } else if (!this.el.hasClass('active')) {
6318 this.el.addClass('active');
6319 if (Roo.bootstrap.version == 4 && this.navLink ) {
6320 this.navLink.addClass('active');
6325 this.fireEvent('changed', this, state);
6328 // show a panel if it's registered and related..
6330 if (!this.navId || !this.tabId || !state || is_was_active) {
6334 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6338 var pan = tg.getPanelByName(this.tabId);
6342 // if we can not flip to new panel - go back to old nav highlight..
6343 if (false == tg.showPanel(pan)) {
6344 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6346 var onav = nv.getWasActive();
6348 onav.setActive(true, false, true);
6357 // this should not be here...
6358 setDisabled : function(state)
6360 this.disabled = state;
6362 this.el.removeClass('disabled');
6363 } else if (!this.el.hasClass('disabled')) {
6364 this.el.addClass('disabled');
6370 * Fetch the element to display the tooltip on.
6371 * @return {Roo.Element} defaults to this.el
6373 tooltipEl : function()
6375 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6378 scrollToElement : function(e)
6380 var c = document.body;
6383 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6385 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6386 c = document.documentElement;
6389 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6395 var o = target.calcOffsetsTo(c);
6402 this.fireEvent('scrollto', this, options, e);
6404 Roo.get(c).scrollTo('top', options.value, true);
6417 * <span> icon </span>
6418 * <span> text </span>
6419 * <span>badge </span>
6423 * @class Roo.bootstrap.NavSidebarItem
6424 * @extends Roo.bootstrap.NavItem
6425 * Bootstrap Navbar.NavSidebarItem class
6426 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6427 * {Boolean} open is the menu open
6428 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6429 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6430 * {String} buttonSize (sm|md|lg)the extra classes for the button
6431 * {Boolean} showArrow show arrow next to the text (default true)
6433 * Create a new Navbar Button
6434 * @param {Object} config The config object
6436 Roo.bootstrap.NavSidebarItem = function(config){
6437 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6442 * The raw click event for the entire grid.
6443 * @param {Roo.EventObject} e
6448 * Fires when the active item active state changes
6449 * @param {Roo.bootstrap.NavSidebarItem} this
6450 * @param {boolean} state the new state
6458 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6460 badgeWeight : 'default',
6466 buttonWeight : 'default',
6472 getAutoCreate : function(){
6477 href : this.href || '#',
6483 if(this.buttonView){
6486 href : this.href || '#',
6487 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6500 cfg.cls += ' active';
6503 if (this.disabled) {
6504 cfg.cls += ' disabled';
6507 cfg.cls += ' open x-open';
6510 if (this.glyphicon || this.icon) {
6511 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6512 a.cn.push({ tag : 'i', cls : c }) ;
6515 if(!this.buttonView){
6518 html : this.html || ''
6525 if (this.badge !== '') {
6526 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6532 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6535 a.cls += ' dropdown-toggle treeview' ;
6541 initEvents : function()
6543 if (typeof (this.menu) != 'undefined') {
6544 this.menu.parentType = this.xtype;
6545 this.menu.triggerEl = this.el;
6546 this.menu = this.addxtype(Roo.apply({}, this.menu));
6549 this.el.on('click', this.onClick, this);
6551 if(this.badge !== ''){
6552 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6557 onClick : function(e)
6564 if(this.preventDefault){
6568 this.fireEvent('click', this, e);
6571 disable : function()
6573 this.setDisabled(true);
6578 this.setDisabled(false);
6581 setDisabled : function(state)
6583 if(this.disabled == state){
6587 this.disabled = state;
6590 this.el.addClass('disabled');
6594 this.el.removeClass('disabled');
6599 setActive : function(state)
6601 if(this.active == state){
6605 this.active = state;
6608 this.el.addClass('active');
6612 this.el.removeClass('active');
6617 isActive: function ()
6622 setBadge : function(str)
6628 this.badgeEl.dom.innerHTML = str;
6643 Roo.namespace('Roo.bootstrap.breadcrumb');
6647 * @class Roo.bootstrap.breadcrumb.Nav
6648 * @extends Roo.bootstrap.Component
6649 * Bootstrap Breadcrumb Nav Class
6651 * @children Roo.bootstrap.breadcrumb.Item
6654 * Create a new breadcrumb.Nav
6655 * @param {Object} config The config object
6659 Roo.bootstrap.breadcrumb.Nav = function(config){
6660 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6665 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6667 getAutoCreate : function()
6684 initEvents: function()
6686 this.olEl = this.el.select('ol',true).first();
6688 getChildContainer : function()
6704 * @class Roo.bootstrap.breadcrumb.Nav
6705 * @extends Roo.bootstrap.Component
6706 * Bootstrap Breadcrumb Nav Class
6708 * @children Roo.bootstrap.breadcrumb.Component
6709 * @cfg {String} html the content of the link.
6710 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6711 * @cfg {Boolean} active is it active
6715 * Create a new breadcrumb.Nav
6716 * @param {Object} config The config object
6719 Roo.bootstrap.breadcrumb.Item = function(config){
6720 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6725 * The img click event for the img.
6726 * @param {Roo.EventObject} e
6733 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6738 getAutoCreate : function()
6743 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6745 if (this.href !== false) {
6752 cfg.html = this.html;
6758 initEvents: function()
6761 this.el.select('a', true).first().on('click',this.onClick, this)
6765 onClick : function(e)
6768 this.fireEvent('click',this, e);
6781 * @class Roo.bootstrap.Row
6782 * @extends Roo.bootstrap.Component
6783 * Bootstrap Row class (contains columns...)
6787 * @param {Object} config The config object
6790 Roo.bootstrap.Row = function(config){
6791 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6794 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6796 getAutoCreate : function(){
6815 * @class Roo.bootstrap.Pagination
6816 * @extends Roo.bootstrap.Component
6817 * Bootstrap Pagination class
6818 * @cfg {String} size xs | sm | md | lg
6819 * @cfg {Boolean} inverse false | true
6822 * Create a new Pagination
6823 * @param {Object} config The config object
6826 Roo.bootstrap.Pagination = function(config){
6827 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6830 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6836 getAutoCreate : function(){
6842 cfg.cls += ' inverse';
6848 cfg.cls += " " + this.cls;
6866 * @class Roo.bootstrap.PaginationItem
6867 * @extends Roo.bootstrap.Component
6868 * Bootstrap PaginationItem class
6869 * @cfg {String} html text
6870 * @cfg {String} href the link
6871 * @cfg {Boolean} preventDefault (true | false) default true
6872 * @cfg {Boolean} active (true | false) default false
6873 * @cfg {Boolean} disabled default false
6877 * Create a new PaginationItem
6878 * @param {Object} config The config object
6882 Roo.bootstrap.PaginationItem = function(config){
6883 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6888 * The raw click event for the entire grid.
6889 * @param {Roo.EventObject} e
6895 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6899 preventDefault: true,
6904 getAutoCreate : function(){
6910 href : this.href ? this.href : '#',
6911 html : this.html ? this.html : ''
6921 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6925 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6931 initEvents: function() {
6933 this.el.on('click', this.onClick, this);
6936 onClick : function(e)
6938 Roo.log('PaginationItem on click ');
6939 if(this.preventDefault){
6947 this.fireEvent('click', this, e);
6963 * @class Roo.bootstrap.Slider
6964 * @extends Roo.bootstrap.Component
6965 * Bootstrap Slider class
6968 * Create a new Slider
6969 * @param {Object} config The config object
6972 Roo.bootstrap.Slider = function(config){
6973 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6976 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6978 getAutoCreate : function(){
6982 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6986 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6998 * Ext JS Library 1.1.1
6999 * Copyright(c) 2006-2007, Ext JS, LLC.
7001 * Originally Released Under LGPL - original licence link has changed is not relivant.
7004 * <script type="text/javascript">
7009 * @class Roo.grid.ColumnModel
7010 * @extends Roo.util.Observable
7011 * This is the default implementation of a ColumnModel used by the Grid. It defines
7012 * the columns in the grid.
7015 var colModel = new Roo.grid.ColumnModel([
7016 {header: "Ticker", width: 60, sortable: true, locked: true},
7017 {header: "Company Name", width: 150, sortable: true},
7018 {header: "Market Cap.", width: 100, sortable: true},
7019 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7020 {header: "Employees", width: 100, sortable: true, resizable: false}
7025 * The config options listed for this class are options which may appear in each
7026 * individual column definition.
7027 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7029 * @param {Object} config An Array of column config objects. See this class's
7030 * config objects for details.
7032 Roo.grid.ColumnModel = function(config){
7034 * The config passed into the constructor
7036 this.config = config;
7039 // if no id, create one
7040 // if the column does not have a dataIndex mapping,
7041 // map it to the order it is in the config
7042 for(var i = 0, len = config.length; i < len; i++){
7044 if(typeof c.dataIndex == "undefined"){
7047 if(typeof c.renderer == "string"){
7048 c.renderer = Roo.util.Format[c.renderer];
7050 if(typeof c.id == "undefined"){
7053 if(c.editor && c.editor.xtype){
7054 c.editor = Roo.factory(c.editor, Roo.grid);
7056 if(c.editor && c.editor.isFormField){
7057 c.editor = new Roo.grid.GridEditor(c.editor);
7059 this.lookup[c.id] = c;
7063 * The width of columns which have no width specified (defaults to 100)
7066 this.defaultWidth = 100;
7069 * Default sortable of columns which have no sortable specified (defaults to false)
7072 this.defaultSortable = false;
7076 * @event widthchange
7077 * Fires when the width of a column changes.
7078 * @param {ColumnModel} this
7079 * @param {Number} columnIndex The column index
7080 * @param {Number} newWidth The new width
7082 "widthchange": true,
7084 * @event headerchange
7085 * Fires when the text of a header changes.
7086 * @param {ColumnModel} this
7087 * @param {Number} columnIndex The column index
7088 * @param {Number} newText The new header text
7090 "headerchange": true,
7092 * @event hiddenchange
7093 * Fires when a column is hidden or "unhidden".
7094 * @param {ColumnModel} this
7095 * @param {Number} columnIndex The column index
7096 * @param {Boolean} hidden true if hidden, false otherwise
7098 "hiddenchange": true,
7100 * @event columnmoved
7101 * Fires when a column is moved.
7102 * @param {ColumnModel} this
7103 * @param {Number} oldIndex
7104 * @param {Number} newIndex
7106 "columnmoved" : true,
7108 * @event columlockchange
7109 * Fires when a column's locked state is changed
7110 * @param {ColumnModel} this
7111 * @param {Number} colIndex
7112 * @param {Boolean} locked true if locked
7114 "columnlockchange" : true
7116 Roo.grid.ColumnModel.superclass.constructor.call(this);
7118 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7120 * @cfg {String} header The header text to display in the Grid view.
7123 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7124 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7125 * specified, the column's index is used as an index into the Record's data Array.
7128 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7129 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7132 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7133 * Defaults to the value of the {@link #defaultSortable} property.
7134 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7137 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7140 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7143 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7146 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7149 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7150 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7151 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7152 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7155 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7158 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7161 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7164 * @cfg {String} cursor (Optional)
7167 * @cfg {String} tooltip (Optional)
7170 * @cfg {Number} xs (Optional)
7173 * @cfg {Number} sm (Optional)
7176 * @cfg {Number} md (Optional)
7179 * @cfg {Number} lg (Optional)
7182 * Returns the id of the column at the specified index.
7183 * @param {Number} index The column index
7184 * @return {String} the id
7186 getColumnId : function(index){
7187 return this.config[index].id;
7191 * Returns the column for a specified id.
7192 * @param {String} id The column id
7193 * @return {Object} the column
7195 getColumnById : function(id){
7196 return this.lookup[id];
7201 * Returns the column for a specified dataIndex.
7202 * @param {String} dataIndex The column dataIndex
7203 * @return {Object|Boolean} the column or false if not found
7205 getColumnByDataIndex: function(dataIndex){
7206 var index = this.findColumnIndex(dataIndex);
7207 return index > -1 ? this.config[index] : false;
7211 * Returns the index for a specified column id.
7212 * @param {String} id The column id
7213 * @return {Number} the index, or -1 if not found
7215 getIndexById : function(id){
7216 for(var i = 0, len = this.config.length; i < len; i++){
7217 if(this.config[i].id == id){
7225 * Returns the index for a specified column dataIndex.
7226 * @param {String} dataIndex The column dataIndex
7227 * @return {Number} the index, or -1 if not found
7230 findColumnIndex : function(dataIndex){
7231 for(var i = 0, len = this.config.length; i < len; i++){
7232 if(this.config[i].dataIndex == dataIndex){
7240 moveColumn : function(oldIndex, newIndex){
7241 var c = this.config[oldIndex];
7242 this.config.splice(oldIndex, 1);
7243 this.config.splice(newIndex, 0, c);
7244 this.dataMap = null;
7245 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7248 isLocked : function(colIndex){
7249 return this.config[colIndex].locked === true;
7252 setLocked : function(colIndex, value, suppressEvent){
7253 if(this.isLocked(colIndex) == value){
7256 this.config[colIndex].locked = value;
7258 this.fireEvent("columnlockchange", this, colIndex, value);
7262 getTotalLockedWidth : function(){
7264 for(var i = 0; i < this.config.length; i++){
7265 if(this.isLocked(i) && !this.isHidden(i)){
7266 this.totalWidth += this.getColumnWidth(i);
7272 getLockedCount : function(){
7273 for(var i = 0, len = this.config.length; i < len; i++){
7274 if(!this.isLocked(i)){
7279 return this.config.length;
7283 * Returns the number of columns.
7286 getColumnCount : function(visibleOnly){
7287 if(visibleOnly === true){
7289 for(var i = 0, len = this.config.length; i < len; i++){
7290 if(!this.isHidden(i)){
7296 return this.config.length;
7300 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7301 * @param {Function} fn
7302 * @param {Object} scope (optional)
7303 * @return {Array} result
7305 getColumnsBy : function(fn, scope){
7307 for(var i = 0, len = this.config.length; i < len; i++){
7308 var c = this.config[i];
7309 if(fn.call(scope||this, c, i) === true){
7317 * Returns true if the specified column is sortable.
7318 * @param {Number} col The column index
7321 isSortable : function(col){
7322 if(typeof this.config[col].sortable == "undefined"){
7323 return this.defaultSortable;
7325 return this.config[col].sortable;
7329 * Returns the rendering (formatting) function defined for the column.
7330 * @param {Number} col The column index.
7331 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7333 getRenderer : function(col){
7334 if(!this.config[col].renderer){
7335 return Roo.grid.ColumnModel.defaultRenderer;
7337 return this.config[col].renderer;
7341 * Sets the rendering (formatting) function for a column.
7342 * @param {Number} col The column index
7343 * @param {Function} fn The function to use to process the cell's raw data
7344 * to return HTML markup for the grid view. The render function is called with
7345 * the following parameters:<ul>
7346 * <li>Data value.</li>
7347 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7348 * <li>css A CSS style string to apply to the table cell.</li>
7349 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7350 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7351 * <li>Row index</li>
7352 * <li>Column index</li>
7353 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7355 setRenderer : function(col, fn){
7356 this.config[col].renderer = fn;
7360 * Returns the width for the specified column.
7361 * @param {Number} col The column index
7364 getColumnWidth : function(col){
7365 return this.config[col].width * 1 || this.defaultWidth;
7369 * Sets the width for a column.
7370 * @param {Number} col The column index
7371 * @param {Number} width The new width
7373 setColumnWidth : function(col, width, suppressEvent){
7374 this.config[col].width = width;
7375 this.totalWidth = null;
7377 this.fireEvent("widthchange", this, col, width);
7382 * Returns the total width of all columns.
7383 * @param {Boolean} includeHidden True to include hidden column widths
7386 getTotalWidth : function(includeHidden){
7387 if(!this.totalWidth){
7388 this.totalWidth = 0;
7389 for(var i = 0, len = this.config.length; i < len; i++){
7390 if(includeHidden || !this.isHidden(i)){
7391 this.totalWidth += this.getColumnWidth(i);
7395 return this.totalWidth;
7399 * Returns the header for the specified column.
7400 * @param {Number} col The column index
7403 getColumnHeader : function(col){
7404 return this.config[col].header;
7408 * Sets the header for a column.
7409 * @param {Number} col The column index
7410 * @param {String} header The new header
7412 setColumnHeader : function(col, header){
7413 this.config[col].header = header;
7414 this.fireEvent("headerchange", this, col, header);
7418 * Returns the tooltip for the specified column.
7419 * @param {Number} col The column index
7422 getColumnTooltip : function(col){
7423 return this.config[col].tooltip;
7426 * Sets the tooltip for a column.
7427 * @param {Number} col The column index
7428 * @param {String} tooltip The new tooltip
7430 setColumnTooltip : function(col, tooltip){
7431 this.config[col].tooltip = tooltip;
7435 * Returns the dataIndex for the specified column.
7436 * @param {Number} col The column index
7439 getDataIndex : function(col){
7440 return this.config[col].dataIndex;
7444 * Sets the dataIndex for a column.
7445 * @param {Number} col The column index
7446 * @param {Number} dataIndex The new dataIndex
7448 setDataIndex : function(col, dataIndex){
7449 this.config[col].dataIndex = dataIndex;
7455 * Returns true if the cell is editable.
7456 * @param {Number} colIndex The column index
7457 * @param {Number} rowIndex The row index - this is nto actually used..?
7460 isCellEditable : function(colIndex, rowIndex){
7461 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7465 * Returns the editor defined for the cell/column.
7466 * return false or null to disable editing.
7467 * @param {Number} colIndex The column index
7468 * @param {Number} rowIndex The row index
7471 getCellEditor : function(colIndex, rowIndex){
7472 return this.config[colIndex].editor;
7476 * Sets if a column is editable.
7477 * @param {Number} col The column index
7478 * @param {Boolean} editable True if the column is editable
7480 setEditable : function(col, editable){
7481 this.config[col].editable = editable;
7486 * Returns true if the column is hidden.
7487 * @param {Number} colIndex The column index
7490 isHidden : function(colIndex){
7491 return this.config[colIndex].hidden;
7496 * Returns true if the column width cannot be changed
7498 isFixed : function(colIndex){
7499 return this.config[colIndex].fixed;
7503 * Returns true if the column can be resized
7506 isResizable : function(colIndex){
7507 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7510 * Sets if a column is hidden.
7511 * @param {Number} colIndex The column index
7512 * @param {Boolean} hidden True if the column is hidden
7514 setHidden : function(colIndex, hidden){
7515 this.config[colIndex].hidden = hidden;
7516 this.totalWidth = null;
7517 this.fireEvent("hiddenchange", this, colIndex, hidden);
7521 * Sets the editor for a column.
7522 * @param {Number} col The column index
7523 * @param {Object} editor The editor object
7525 setEditor : function(col, editor){
7526 this.config[col].editor = editor;
7530 Roo.grid.ColumnModel.defaultRenderer = function(value)
7532 if(typeof value == "object") {
7535 if(typeof value == "string" && value.length < 1){
7539 return String.format("{0}", value);
7542 // Alias for backwards compatibility
7543 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7546 * Ext JS Library 1.1.1
7547 * Copyright(c) 2006-2007, Ext JS, LLC.
7549 * Originally Released Under LGPL - original licence link has changed is not relivant.
7552 * <script type="text/javascript">
7556 * @class Roo.LoadMask
7557 * A simple utility class for generically masking elements while loading data. If the element being masked has
7558 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7559 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7560 * element's UpdateManager load indicator and will be destroyed after the initial load.
7562 * Create a new LoadMask
7563 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7564 * @param {Object} config The config object
7566 Roo.LoadMask = function(el, config){
7567 this.el = Roo.get(el);
7568 Roo.apply(this, config);
7570 this.store.on('beforeload', this.onBeforeLoad, this);
7571 this.store.on('load', this.onLoad, this);
7572 this.store.on('loadexception', this.onLoadException, this);
7573 this.removeMask = false;
7575 var um = this.el.getUpdateManager();
7576 um.showLoadIndicator = false; // disable the default indicator
7577 um.on('beforeupdate', this.onBeforeLoad, this);
7578 um.on('update', this.onLoad, this);
7579 um.on('failure', this.onLoad, this);
7580 this.removeMask = true;
7584 Roo.LoadMask.prototype = {
7586 * @cfg {Boolean} removeMask
7587 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7588 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7592 * The text to display in a centered loading message box (defaults to 'Loading...')
7596 * @cfg {String} msgCls
7597 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7599 msgCls : 'x-mask-loading',
7602 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7608 * Disables the mask to prevent it from being displayed
7610 disable : function(){
7611 this.disabled = true;
7615 * Enables the mask so that it can be displayed
7617 enable : function(){
7618 this.disabled = false;
7621 onLoadException : function()
7625 if (typeof(arguments[3]) != 'undefined') {
7626 Roo.MessageBox.alert("Error loading",arguments[3]);
7630 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7631 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7638 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7643 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7647 onBeforeLoad : function(){
7649 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7654 destroy : function(){
7656 this.store.un('beforeload', this.onBeforeLoad, this);
7657 this.store.un('load', this.onLoad, this);
7658 this.store.un('loadexception', this.onLoadException, this);
7660 var um = this.el.getUpdateManager();
7661 um.un('beforeupdate', this.onBeforeLoad, this);
7662 um.un('update', this.onLoad, this);
7663 um.un('failure', this.onLoad, this);
7674 * @class Roo.bootstrap.Table
7675 * @extends Roo.bootstrap.Component
7676 * Bootstrap Table class
7677 * @cfg {String} cls table class
7678 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7679 * @cfg {String} bgcolor Specifies the background color for a table
7680 * @cfg {Number} border Specifies whether the table cells should have borders or not
7681 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7682 * @cfg {Number} cellspacing Specifies the space between cells
7683 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7684 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7685 * @cfg {String} sortable Specifies that the table should be sortable
7686 * @cfg {String} summary Specifies a summary of the content of a table
7687 * @cfg {Number} width Specifies the width of a table
7688 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7690 * @cfg {boolean} striped Should the rows be alternative striped
7691 * @cfg {boolean} bordered Add borders to the table
7692 * @cfg {boolean} hover Add hover highlighting
7693 * @cfg {boolean} condensed Format condensed
7694 * @cfg {boolean} responsive Format condensed
7695 * @cfg {Boolean} loadMask (true|false) default false
7696 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7697 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7698 * @cfg {Boolean} rowSelection (true|false) default false
7699 * @cfg {Boolean} cellSelection (true|false) default false
7700 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7701 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7702 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7703 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7707 * Create a new Table
7708 * @param {Object} config The config object
7711 Roo.bootstrap.Table = function(config){
7712 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7717 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7718 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7719 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7720 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7722 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7724 this.sm.grid = this;
7725 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7726 this.sm = this.selModel;
7727 this.sm.xmodule = this.xmodule || false;
7730 if (this.cm && typeof(this.cm.config) == 'undefined') {
7731 this.colModel = new Roo.grid.ColumnModel(this.cm);
7732 this.cm = this.colModel;
7733 this.cm.xmodule = this.xmodule || false;
7736 this.store= Roo.factory(this.store, Roo.data);
7737 this.ds = this.store;
7738 this.ds.xmodule = this.xmodule || false;
7741 if (this.footer && this.store) {
7742 this.footer.dataSource = this.ds;
7743 this.footer = Roo.factory(this.footer);
7750 * Fires when a cell is clicked
7751 * @param {Roo.bootstrap.Table} this
7752 * @param {Roo.Element} el
7753 * @param {Number} rowIndex
7754 * @param {Number} columnIndex
7755 * @param {Roo.EventObject} e
7759 * @event celldblclick
7760 * Fires when a cell is double clicked
7761 * @param {Roo.bootstrap.Table} this
7762 * @param {Roo.Element} el
7763 * @param {Number} rowIndex
7764 * @param {Number} columnIndex
7765 * @param {Roo.EventObject} e
7767 "celldblclick" : true,
7770 * Fires when a row is clicked
7771 * @param {Roo.bootstrap.Table} this
7772 * @param {Roo.Element} el
7773 * @param {Number} rowIndex
7774 * @param {Roo.EventObject} e
7778 * @event rowdblclick
7779 * Fires when a row is double clicked
7780 * @param {Roo.bootstrap.Table} this
7781 * @param {Roo.Element} el
7782 * @param {Number} rowIndex
7783 * @param {Roo.EventObject} e
7785 "rowdblclick" : true,
7788 * Fires when a mouseover occur
7789 * @param {Roo.bootstrap.Table} this
7790 * @param {Roo.Element} el
7791 * @param {Number} rowIndex
7792 * @param {Number} columnIndex
7793 * @param {Roo.EventObject} e
7798 * Fires when a mouseout occur
7799 * @param {Roo.bootstrap.Table} this
7800 * @param {Roo.Element} el
7801 * @param {Number} rowIndex
7802 * @param {Number} columnIndex
7803 * @param {Roo.EventObject} e
7808 * Fires when a row is rendered, so you can change add a style to it.
7809 * @param {Roo.bootstrap.Table} this
7810 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7814 * @event rowsrendered
7815 * Fires when all the rows have been rendered
7816 * @param {Roo.bootstrap.Table} this
7818 'rowsrendered' : true,
7820 * @event contextmenu
7821 * The raw contextmenu event for the entire grid.
7822 * @param {Roo.EventObject} e
7824 "contextmenu" : true,
7826 * @event rowcontextmenu
7827 * Fires when a row is right clicked
7828 * @param {Roo.bootstrap.Table} this
7829 * @param {Number} rowIndex
7830 * @param {Roo.EventObject} e
7832 "rowcontextmenu" : true,
7834 * @event cellcontextmenu
7835 * Fires when a cell is right clicked
7836 * @param {Roo.bootstrap.Table} this
7837 * @param {Number} rowIndex
7838 * @param {Number} cellIndex
7839 * @param {Roo.EventObject} e
7841 "cellcontextmenu" : true,
7843 * @event headercontextmenu
7844 * Fires when a header is right clicked
7845 * @param {Roo.bootstrap.Table} this
7846 * @param {Number} columnIndex
7847 * @param {Roo.EventObject} e
7849 "headercontextmenu" : true
7853 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7879 rowSelection : false,
7880 cellSelection : false,
7883 // Roo.Element - the tbody
7885 // Roo.Element - thead element
7888 container: false, // used by gridpanel...
7894 auto_hide_footer : false,
7896 getAutoCreate : function()
7898 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7905 if (this.scrollBody) {
7906 cfg.cls += ' table-body-fixed';
7909 cfg.cls += ' table-striped';
7913 cfg.cls += ' table-hover';
7915 if (this.bordered) {
7916 cfg.cls += ' table-bordered';
7918 if (this.condensed) {
7919 cfg.cls += ' table-condensed';
7921 if (this.responsive) {
7922 cfg.cls += ' table-responsive';
7926 cfg.cls+= ' ' +this.cls;
7929 // this lot should be simplifed...
7942 ].forEach(function(k) {
7950 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7953 if(this.store || this.cm){
7954 if(this.headerShow){
7955 cfg.cn.push(this.renderHeader());
7958 cfg.cn.push(this.renderBody());
7960 if(this.footerShow){
7961 cfg.cn.push(this.renderFooter());
7963 // where does this come from?
7964 //cfg.cls+= ' TableGrid';
7967 return { cn : [ cfg ] };
7970 initEvents : function()
7972 if(!this.store || !this.cm){
7975 if (this.selModel) {
7976 this.selModel.initEvents();
7980 //Roo.log('initEvents with ds!!!!');
7982 this.mainBody = this.el.select('tbody', true).first();
7983 this.mainHead = this.el.select('thead', true).first();
7984 this.mainFoot = this.el.select('tfoot', true).first();
7990 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7991 e.on('click', _this.sort, _this);
7994 this.mainBody.on("click", this.onClick, this);
7995 this.mainBody.on("dblclick", this.onDblClick, this);
7997 // why is this done????? = it breaks dialogs??
7998 //this.parent().el.setStyle('position', 'relative');
8002 this.footer.parentId = this.id;
8003 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8006 this.el.select('tfoot tr td').first().addClass('hide');
8011 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8014 this.store.on('load', this.onLoad, this);
8015 this.store.on('beforeload', this.onBeforeLoad, this);
8016 this.store.on('update', this.onUpdate, this);
8017 this.store.on('add', this.onAdd, this);
8018 this.store.on("clear", this.clear, this);
8020 this.el.on("contextmenu", this.onContextMenu, this);
8022 this.mainBody.on('scroll', this.onBodyScroll, this);
8024 this.cm.on("headerchange", this.onHeaderChange, this);
8026 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8030 onContextMenu : function(e, t)
8032 this.processEvent("contextmenu", e);
8035 processEvent : function(name, e)
8037 if (name != 'touchstart' ) {
8038 this.fireEvent(name, e);
8041 var t = e.getTarget();
8043 var cell = Roo.get(t);
8049 if(cell.findParent('tfoot', false, true)){
8053 if(cell.findParent('thead', false, true)){
8055 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8056 cell = Roo.get(t).findParent('th', false, true);
8058 Roo.log("failed to find th in thead?");
8059 Roo.log(e.getTarget());
8064 var cellIndex = cell.dom.cellIndex;
8066 var ename = name == 'touchstart' ? 'click' : name;
8067 this.fireEvent("header" + ename, this, cellIndex, e);
8072 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8073 cell = Roo.get(t).findParent('td', false, true);
8075 Roo.log("failed to find th in tbody?");
8076 Roo.log(e.getTarget());
8081 var row = cell.findParent('tr', false, true);
8082 var cellIndex = cell.dom.cellIndex;
8083 var rowIndex = row.dom.rowIndex - 1;
8087 this.fireEvent("row" + name, this, rowIndex, e);
8091 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8097 onMouseover : function(e, el)
8099 var cell = Roo.get(el);
8105 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8106 cell = cell.findParent('td', false, true);
8109 var row = cell.findParent('tr', false, true);
8110 var cellIndex = cell.dom.cellIndex;
8111 var rowIndex = row.dom.rowIndex - 1; // start from 0
8113 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8117 onMouseout : function(e, el)
8119 var cell = Roo.get(el);
8125 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8126 cell = cell.findParent('td', false, true);
8129 var row = cell.findParent('tr', false, true);
8130 var cellIndex = cell.dom.cellIndex;
8131 var rowIndex = row.dom.rowIndex - 1; // start from 0
8133 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8137 onClick : function(e, el)
8139 var cell = Roo.get(el);
8141 if(!cell || (!this.cellSelection && !this.rowSelection)){
8145 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8146 cell = cell.findParent('td', false, true);
8149 if(!cell || typeof(cell) == 'undefined'){
8153 var row = cell.findParent('tr', false, true);
8155 if(!row || typeof(row) == 'undefined'){
8159 var cellIndex = cell.dom.cellIndex;
8160 var rowIndex = this.getRowIndex(row);
8162 // why??? - should these not be based on SelectionModel?
8163 if(this.cellSelection){
8164 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8167 if(this.rowSelection){
8168 this.fireEvent('rowclick', this, row, rowIndex, e);
8174 onDblClick : function(e,el)
8176 var cell = Roo.get(el);
8178 if(!cell || (!this.cellSelection && !this.rowSelection)){
8182 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8183 cell = cell.findParent('td', false, true);
8186 if(!cell || typeof(cell) == 'undefined'){
8190 var row = cell.findParent('tr', false, true);
8192 if(!row || typeof(row) == 'undefined'){
8196 var cellIndex = cell.dom.cellIndex;
8197 var rowIndex = this.getRowIndex(row);
8199 if(this.cellSelection){
8200 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8203 if(this.rowSelection){
8204 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8208 sort : function(e,el)
8210 var col = Roo.get(el);
8212 if(!col.hasClass('sortable')){
8216 var sort = col.attr('sort');
8219 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8223 this.store.sortInfo = {field : sort, direction : dir};
8226 Roo.log("calling footer first");
8227 this.footer.onClick('first');
8230 this.store.load({ params : { start : 0 } });
8234 renderHeader : function()
8242 this.totalWidth = 0;
8244 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8246 var config = cm.config[i];
8250 cls : 'x-hcol-' + i,
8252 html: cm.getColumnHeader(i)
8257 if(typeof(config.sortable) != 'undefined' && config.sortable){
8259 c.html = '<i class="glyphicon"></i>' + c.html;
8262 // could use BS4 hidden-..-down
8264 if(typeof(config.lgHeader) != 'undefined'){
8265 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8268 if(typeof(config.mdHeader) != 'undefined'){
8269 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8272 if(typeof(config.smHeader) != 'undefined'){
8273 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8276 if(typeof(config.xsHeader) != 'undefined'){
8277 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8284 if(typeof(config.tooltip) != 'undefined'){
8285 c.tooltip = config.tooltip;
8288 if(typeof(config.colspan) != 'undefined'){
8289 c.colspan = config.colspan;
8292 if(typeof(config.hidden) != 'undefined' && config.hidden){
8293 c.style += ' display:none;';
8296 if(typeof(config.dataIndex) != 'undefined'){
8297 c.sort = config.dataIndex;
8302 if(typeof(config.align) != 'undefined' && config.align.length){
8303 c.style += ' text-align:' + config.align + ';';
8306 if(typeof(config.width) != 'undefined'){
8307 c.style += ' width:' + config.width + 'px;';
8308 this.totalWidth += config.width;
8310 this.totalWidth += 100; // assume minimum of 100 per column?
8313 if(typeof(config.cls) != 'undefined'){
8314 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8317 ['xs','sm','md','lg'].map(function(size){
8319 if(typeof(config[size]) == 'undefined'){
8323 if (!config[size]) { // 0 = hidden
8324 // BS 4 '0' is treated as hide that column and below.
8325 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8329 c.cls += ' col-' + size + '-' + config[size] + (
8330 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8342 renderBody : function()
8352 colspan : this.cm.getColumnCount()
8362 renderFooter : function()
8372 colspan : this.cm.getColumnCount()
8386 // Roo.log('ds onload');
8391 var ds = this.store;
8393 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8394 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8395 if (_this.store.sortInfo) {
8397 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8398 e.select('i', true).addClass(['glyphicon-arrow-up']);
8401 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8402 e.select('i', true).addClass(['glyphicon-arrow-down']);
8407 var tbody = this.mainBody;
8409 if(ds.getCount() > 0){
8410 ds.data.each(function(d,rowIndex){
8411 var row = this.renderRow(cm, ds, rowIndex);
8413 tbody.createChild(row);
8417 if(row.cellObjects.length){
8418 Roo.each(row.cellObjects, function(r){
8419 _this.renderCellObject(r);
8426 var tfoot = this.el.select('tfoot', true).first();
8428 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8430 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8432 var total = this.ds.getTotalCount();
8434 if(this.footer.pageSize < total){
8435 this.mainFoot.show();
8439 Roo.each(this.el.select('tbody td', true).elements, function(e){
8440 e.on('mouseover', _this.onMouseover, _this);
8443 Roo.each(this.el.select('tbody td', true).elements, function(e){
8444 e.on('mouseout', _this.onMouseout, _this);
8446 this.fireEvent('rowsrendered', this);
8452 onUpdate : function(ds,record)
8454 this.refreshRow(record);
8458 onRemove : function(ds, record, index, isUpdate){
8459 if(isUpdate !== true){
8460 this.fireEvent("beforerowremoved", this, index, record);
8462 var bt = this.mainBody.dom;
8464 var rows = this.el.select('tbody > tr', true).elements;
8466 if(typeof(rows[index]) != 'undefined'){
8467 bt.removeChild(rows[index].dom);
8470 // if(bt.rows[index]){
8471 // bt.removeChild(bt.rows[index]);
8474 if(isUpdate !== true){
8475 //this.stripeRows(index);
8476 //this.syncRowHeights(index, index);
8478 this.fireEvent("rowremoved", this, index, record);
8482 onAdd : function(ds, records, rowIndex)
8484 //Roo.log('on Add called');
8485 // - note this does not handle multiple adding very well..
8486 var bt = this.mainBody.dom;
8487 for (var i =0 ; i < records.length;i++) {
8488 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8489 //Roo.log(records[i]);
8490 //Roo.log(this.store.getAt(rowIndex+i));
8491 this.insertRow(this.store, rowIndex + i, false);
8498 refreshRow : function(record){
8499 var ds = this.store, index;
8500 if(typeof record == 'number'){
8502 record = ds.getAt(index);
8504 index = ds.indexOf(record);
8506 return; // should not happen - but seems to
8509 this.insertRow(ds, index, true);
8511 this.onRemove(ds, record, index+1, true);
8513 //this.syncRowHeights(index, index);
8515 this.fireEvent("rowupdated", this, index, record);
8518 insertRow : function(dm, rowIndex, isUpdate){
8521 this.fireEvent("beforerowsinserted", this, rowIndex);
8523 //var s = this.getScrollState();
8524 var row = this.renderRow(this.cm, this.store, rowIndex);
8525 // insert before rowIndex..
8526 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8530 if(row.cellObjects.length){
8531 Roo.each(row.cellObjects, function(r){
8532 _this.renderCellObject(r);
8537 this.fireEvent("rowsinserted", this, rowIndex);
8538 //this.syncRowHeights(firstRow, lastRow);
8539 //this.stripeRows(firstRow);
8546 getRowDom : function(rowIndex)
8548 var rows = this.el.select('tbody > tr', true).elements;
8550 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8553 // returns the object tree for a tr..
8556 renderRow : function(cm, ds, rowIndex)
8558 var d = ds.getAt(rowIndex);
8562 cls : 'x-row-' + rowIndex,
8566 var cellObjects = [];
8568 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8569 var config = cm.config[i];
8571 var renderer = cm.getRenderer(i);
8575 if(typeof(renderer) !== 'undefined'){
8576 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8578 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8579 // and are rendered into the cells after the row is rendered - using the id for the element.
8581 if(typeof(value) === 'object'){
8591 rowIndex : rowIndex,
8596 this.fireEvent('rowclass', this, rowcfg);
8600 cls : rowcfg.rowClass + ' x-col-' + i,
8602 html: (typeof(value) === 'object') ? '' : value
8609 if(typeof(config.colspan) != 'undefined'){
8610 td.colspan = config.colspan;
8613 if(typeof(config.hidden) != 'undefined' && config.hidden){
8614 td.style += ' display:none;';
8617 if(typeof(config.align) != 'undefined' && config.align.length){
8618 td.style += ' text-align:' + config.align + ';';
8620 if(typeof(config.valign) != 'undefined' && config.valign.length){
8621 td.style += ' vertical-align:' + config.valign + ';';
8624 if(typeof(config.width) != 'undefined'){
8625 td.style += ' width:' + config.width + 'px;';
8628 if(typeof(config.cursor) != 'undefined'){
8629 td.style += ' cursor:' + config.cursor + ';';
8632 if(typeof(config.cls) != 'undefined'){
8633 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8636 ['xs','sm','md','lg'].map(function(size){
8638 if(typeof(config[size]) == 'undefined'){
8644 if (!config[size]) { // 0 = hidden
8645 // BS 4 '0' is treated as hide that column and below.
8646 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8650 td.cls += ' col-' + size + '-' + config[size] + (
8651 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8661 row.cellObjects = cellObjects;
8669 onBeforeLoad : function()
8678 this.el.select('tbody', true).first().dom.innerHTML = '';
8681 * Show or hide a row.
8682 * @param {Number} rowIndex to show or hide
8683 * @param {Boolean} state hide
8685 setRowVisibility : function(rowIndex, state)
8687 var bt = this.mainBody.dom;
8689 var rows = this.el.select('tbody > tr', true).elements;
8691 if(typeof(rows[rowIndex]) == 'undefined'){
8694 rows[rowIndex].dom.style.display = state ? '' : 'none';
8698 getSelectionModel : function(){
8700 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8702 return this.selModel;
8705 * Render the Roo.bootstrap object from renderder
8707 renderCellObject : function(r)
8711 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8713 var t = r.cfg.render(r.container);
8716 Roo.each(r.cfg.cn, function(c){
8718 container: t.getChildContainer(),
8721 _this.renderCellObject(child);
8726 getRowIndex : function(row)
8730 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8741 * Returns the grid's underlying element = used by panel.Grid
8742 * @return {Element} The element
8744 getGridEl : function(){
8748 * Forces a resize - used by panel.Grid
8749 * @return {Element} The element
8751 autoSize : function()
8753 //var ctr = Roo.get(this.container.dom.parentElement);
8754 var ctr = Roo.get(this.el.dom);
8756 var thd = this.getGridEl().select('thead',true).first();
8757 var tbd = this.getGridEl().select('tbody', true).first();
8758 var tfd = this.getGridEl().select('tfoot', true).first();
8760 var cw = ctr.getWidth();
8761 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8765 tbd.setWidth(ctr.getWidth());
8766 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8767 // this needs fixing for various usage - currently only hydra job advers I think..
8769 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8771 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8774 cw = Math.max(cw, this.totalWidth);
8775 this.getGridEl().select('tbody tr',true).setWidth(cw);
8777 // resize 'expandable coloumn?
8779 return; // we doe not have a view in this design..
8782 onBodyScroll: function()
8784 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8786 this.mainHead.setStyle({
8787 'position' : 'relative',
8788 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8794 var scrollHeight = this.mainBody.dom.scrollHeight;
8796 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8798 var height = this.mainBody.getHeight();
8800 if(scrollHeight - height == scrollTop) {
8802 var total = this.ds.getTotalCount();
8804 if(this.footer.cursor + this.footer.pageSize < total){
8806 this.footer.ds.load({
8808 start : this.footer.cursor + this.footer.pageSize,
8809 limit : this.footer.pageSize
8819 onHeaderChange : function()
8821 var header = this.renderHeader();
8822 var table = this.el.select('table', true).first();
8824 this.mainHead.remove();
8825 this.mainHead = table.createChild(header, this.mainBody, false);
8828 onHiddenChange : function(colModel, colIndex, hidden)
8830 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8831 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8833 this.CSS.updateRule(thSelector, "display", "");
8834 this.CSS.updateRule(tdSelector, "display", "");
8837 this.CSS.updateRule(thSelector, "display", "none");
8838 this.CSS.updateRule(tdSelector, "display", "none");
8841 this.onHeaderChange();
8845 setColumnWidth: function(col_index, width)
8847 // width = "md-2 xs-2..."
8848 if(!this.colModel.config[col_index]) {
8852 var w = width.split(" ");
8854 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8856 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8859 for(var j = 0; j < w.length; j++) {
8865 var size_cls = w[j].split("-");
8867 if(!Number.isInteger(size_cls[1] * 1)) {
8871 if(!this.colModel.config[col_index][size_cls[0]]) {
8875 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8879 h_row[0].classList.replace(
8880 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8881 "col-"+size_cls[0]+"-"+size_cls[1]
8884 for(var i = 0; i < rows.length; i++) {
8886 var size_cls = w[j].split("-");
8888 if(!Number.isInteger(size_cls[1] * 1)) {
8892 if(!this.colModel.config[col_index][size_cls[0]]) {
8896 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8900 rows[i].classList.replace(
8901 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8902 "col-"+size_cls[0]+"-"+size_cls[1]
8906 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8921 * @class Roo.bootstrap.TableCell
8922 * @extends Roo.bootstrap.Component
8923 * Bootstrap TableCell class
8924 * @cfg {String} html cell contain text
8925 * @cfg {String} cls cell class
8926 * @cfg {String} tag cell tag (td|th) default td
8927 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8928 * @cfg {String} align Aligns the content in a cell
8929 * @cfg {String} axis Categorizes cells
8930 * @cfg {String} bgcolor Specifies the background color of a cell
8931 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8932 * @cfg {Number} colspan Specifies the number of columns a cell should span
8933 * @cfg {String} headers Specifies one or more header cells a cell is related to
8934 * @cfg {Number} height Sets the height of a cell
8935 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8936 * @cfg {Number} rowspan Sets the number of rows a cell should span
8937 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8938 * @cfg {String} valign Vertical aligns the content in a cell
8939 * @cfg {Number} width Specifies the width of a cell
8942 * Create a new TableCell
8943 * @param {Object} config The config object
8946 Roo.bootstrap.TableCell = function(config){
8947 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8950 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8970 getAutoCreate : function(){
8971 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8991 cfg.align=this.align
8997 cfg.bgcolor=this.bgcolor
9000 cfg.charoff=this.charoff
9003 cfg.colspan=this.colspan
9006 cfg.headers=this.headers
9009 cfg.height=this.height
9012 cfg.nowrap=this.nowrap
9015 cfg.rowspan=this.rowspan
9018 cfg.scope=this.scope
9021 cfg.valign=this.valign
9024 cfg.width=this.width
9043 * @class Roo.bootstrap.TableRow
9044 * @extends Roo.bootstrap.Component
9045 * Bootstrap TableRow class
9046 * @cfg {String} cls row class
9047 * @cfg {String} align Aligns the content in a table row
9048 * @cfg {String} bgcolor Specifies a background color for a table row
9049 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9050 * @cfg {String} valign Vertical aligns the content in a table row
9053 * Create a new TableRow
9054 * @param {Object} config The config object
9057 Roo.bootstrap.TableRow = function(config){
9058 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9061 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9069 getAutoCreate : function(){
9070 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9080 cfg.align = this.align;
9083 cfg.bgcolor = this.bgcolor;
9086 cfg.charoff = this.charoff;
9089 cfg.valign = this.valign;
9107 * @class Roo.bootstrap.TableBody
9108 * @extends Roo.bootstrap.Component
9109 * Bootstrap TableBody class
9110 * @cfg {String} cls element class
9111 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9112 * @cfg {String} align Aligns the content inside the element
9113 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9114 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9117 * Create a new TableBody
9118 * @param {Object} config The config object
9121 Roo.bootstrap.TableBody = function(config){
9122 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9125 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9133 getAutoCreate : function(){
9134 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9148 cfg.align = this.align;
9151 cfg.charoff = this.charoff;
9154 cfg.valign = this.valign;
9161 // initEvents : function()
9168 // this.store = Roo.factory(this.store, Roo.data);
9169 // this.store.on('load', this.onLoad, this);
9171 // this.store.load();
9175 // onLoad: function ()
9177 // this.fireEvent('load', this);
9187 * Ext JS Library 1.1.1
9188 * Copyright(c) 2006-2007, Ext JS, LLC.
9190 * Originally Released Under LGPL - original licence link has changed is not relivant.
9193 * <script type="text/javascript">
9196 // as we use this in bootstrap.
9197 Roo.namespace('Roo.form');
9199 * @class Roo.form.Action
9200 * Internal Class used to handle form actions
9202 * @param {Roo.form.BasicForm} el The form element or its id
9203 * @param {Object} config Configuration options
9208 // define the action interface
9209 Roo.form.Action = function(form, options){
9211 this.options = options || {};
9214 * Client Validation Failed
9217 Roo.form.Action.CLIENT_INVALID = 'client';
9219 * Server Validation Failed
9222 Roo.form.Action.SERVER_INVALID = 'server';
9224 * Connect to Server Failed
9227 Roo.form.Action.CONNECT_FAILURE = 'connect';
9229 * Reading Data from Server Failed
9232 Roo.form.Action.LOAD_FAILURE = 'load';
9234 Roo.form.Action.prototype = {
9236 failureType : undefined,
9237 response : undefined,
9241 run : function(options){
9246 success : function(response){
9251 handleResponse : function(response){
9255 // default connection failure
9256 failure : function(response){
9258 this.response = response;
9259 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9260 this.form.afterAction(this, false);
9263 processResponse : function(response){
9264 this.response = response;
9265 if(!response.responseText){
9268 this.result = this.handleResponse(response);
9272 // utility functions used internally
9273 getUrl : function(appendParams){
9274 var url = this.options.url || this.form.url || this.form.el.dom.action;
9276 var p = this.getParams();
9278 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9284 getMethod : function(){
9285 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9288 getParams : function(){
9289 var bp = this.form.baseParams;
9290 var p = this.options.params;
9292 if(typeof p == "object"){
9293 p = Roo.urlEncode(Roo.applyIf(p, bp));
9294 }else if(typeof p == 'string' && bp){
9295 p += '&' + Roo.urlEncode(bp);
9298 p = Roo.urlEncode(bp);
9303 createCallback : function(){
9305 success: this.success,
9306 failure: this.failure,
9308 timeout: (this.form.timeout*1000),
9309 upload: this.form.fileUpload ? this.success : undefined
9314 Roo.form.Action.Submit = function(form, options){
9315 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9318 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9321 haveProgress : false,
9322 uploadComplete : false,
9324 // uploadProgress indicator.
9325 uploadProgress : function()
9327 if (!this.form.progressUrl) {
9331 if (!this.haveProgress) {
9332 Roo.MessageBox.progress("Uploading", "Uploading");
9334 if (this.uploadComplete) {
9335 Roo.MessageBox.hide();
9339 this.haveProgress = true;
9341 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9343 var c = new Roo.data.Connection();
9345 url : this.form.progressUrl,
9350 success : function(req){
9351 //console.log(data);
9355 rdata = Roo.decode(req.responseText)
9357 Roo.log("Invalid data from server..");
9361 if (!rdata || !rdata.success) {
9363 Roo.MessageBox.alert(Roo.encode(rdata));
9366 var data = rdata.data;
9368 if (this.uploadComplete) {
9369 Roo.MessageBox.hide();
9374 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9375 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9378 this.uploadProgress.defer(2000,this);
9381 failure: function(data) {
9382 Roo.log('progress url failed ');
9393 // run get Values on the form, so it syncs any secondary forms.
9394 this.form.getValues();
9396 var o = this.options;
9397 var method = this.getMethod();
9398 var isPost = method == 'POST';
9399 if(o.clientValidation === false || this.form.isValid()){
9401 if (this.form.progressUrl) {
9402 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9403 (new Date() * 1) + '' + Math.random());
9408 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9409 form:this.form.el.dom,
9410 url:this.getUrl(!isPost),
9412 params:isPost ? this.getParams() : null,
9413 isUpload: this.form.fileUpload,
9414 formData : this.form.formData
9417 this.uploadProgress();
9419 }else if (o.clientValidation !== false){ // client validation failed
9420 this.failureType = Roo.form.Action.CLIENT_INVALID;
9421 this.form.afterAction(this, false);
9425 success : function(response)
9427 this.uploadComplete= true;
9428 if (this.haveProgress) {
9429 Roo.MessageBox.hide();
9433 var result = this.processResponse(response);
9434 if(result === true || result.success){
9435 this.form.afterAction(this, true);
9439 this.form.markInvalid(result.errors);
9440 this.failureType = Roo.form.Action.SERVER_INVALID;
9442 this.form.afterAction(this, false);
9444 failure : function(response)
9446 this.uploadComplete= true;
9447 if (this.haveProgress) {
9448 Roo.MessageBox.hide();
9451 this.response = response;
9452 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9453 this.form.afterAction(this, false);
9456 handleResponse : function(response){
9457 if(this.form.errorReader){
9458 var rs = this.form.errorReader.read(response);
9461 for(var i = 0, len = rs.records.length; i < len; i++) {
9462 var r = rs.records[i];
9466 if(errors.length < 1){
9470 success : rs.success,
9476 ret = Roo.decode(response.responseText);
9480 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9490 Roo.form.Action.Load = function(form, options){
9491 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9492 this.reader = this.form.reader;
9495 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9500 Roo.Ajax.request(Roo.apply(
9501 this.createCallback(), {
9502 method:this.getMethod(),
9503 url:this.getUrl(false),
9504 params:this.getParams()
9508 success : function(response){
9510 var result = this.processResponse(response);
9511 if(result === true || !result.success || !result.data){
9512 this.failureType = Roo.form.Action.LOAD_FAILURE;
9513 this.form.afterAction(this, false);
9516 this.form.clearInvalid();
9517 this.form.setValues(result.data);
9518 this.form.afterAction(this, true);
9521 handleResponse : function(response){
9522 if(this.form.reader){
9523 var rs = this.form.reader.read(response);
9524 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9526 success : rs.success,
9530 return Roo.decode(response.responseText);
9534 Roo.form.Action.ACTION_TYPES = {
9535 'load' : Roo.form.Action.Load,
9536 'submit' : Roo.form.Action.Submit
9545 * @class Roo.bootstrap.Form
9546 * @extends Roo.bootstrap.Component
9547 * Bootstrap Form class
9548 * @cfg {String} method GET | POST (default POST)
9549 * @cfg {String} labelAlign top | left (default top)
9550 * @cfg {String} align left | right - for navbars
9551 * @cfg {Boolean} loadMask load mask when submit (default true)
9556 * @param {Object} config The config object
9560 Roo.bootstrap.Form = function(config){
9562 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9564 Roo.bootstrap.Form.popover.apply();
9568 * @event clientvalidation
9569 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9570 * @param {Form} this
9571 * @param {Boolean} valid true if the form has passed client-side validation
9573 clientvalidation: true,
9575 * @event beforeaction
9576 * Fires before any action is performed. Return false to cancel the action.
9577 * @param {Form} this
9578 * @param {Action} action The action to be performed
9582 * @event actionfailed
9583 * Fires when an action fails.
9584 * @param {Form} this
9585 * @param {Action} action The action that failed
9587 actionfailed : true,
9589 * @event actioncomplete
9590 * Fires when an action is completed.
9591 * @param {Form} this
9592 * @param {Action} action The action that completed
9594 actioncomplete : true
9598 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9601 * @cfg {String} method
9602 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9607 * The URL to use for form actions if one isn't supplied in the action options.
9610 * @cfg {Boolean} fileUpload
9611 * Set to true if this form is a file upload.
9615 * @cfg {Object} baseParams
9616 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9620 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9624 * @cfg {Sting} align (left|right) for navbar forms
9629 activeAction : null,
9632 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9633 * element by passing it or its id or mask the form itself by passing in true.
9636 waitMsgTarget : false,
9641 * @cfg {Boolean} errorMask (true|false) default false
9646 * @cfg {Number} maskOffset Default 100
9651 * @cfg {Boolean} maskBody
9655 getAutoCreate : function(){
9659 method : this.method || 'POST',
9660 id : this.id || Roo.id(),
9663 if (this.parent().xtype.match(/^Nav/)) {
9664 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9668 if (this.labelAlign == 'left' ) {
9669 cfg.cls += ' form-horizontal';
9675 initEvents : function()
9677 this.el.on('submit', this.onSubmit, this);
9678 // this was added as random key presses on the form where triggering form submit.
9679 this.el.on('keypress', function(e) {
9680 if (e.getCharCode() != 13) {
9683 // we might need to allow it for textareas.. and some other items.
9684 // check e.getTarget().
9686 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9690 Roo.log("keypress blocked");
9698 onSubmit : function(e){
9703 * Returns true if client-side validation on the form is successful.
9706 isValid : function(){
9707 var items = this.getItems();
9711 items.each(function(f){
9717 Roo.log('invalid field: ' + f.name);
9721 if(!target && f.el.isVisible(true)){
9727 if(this.errorMask && !valid){
9728 Roo.bootstrap.Form.popover.mask(this, target);
9735 * Returns true if any fields in this form have changed since their original load.
9738 isDirty : function(){
9740 var items = this.getItems();
9741 items.each(function(f){
9751 * Performs a predefined action (submit or load) or custom actions you define on this form.
9752 * @param {String} actionName The name of the action type
9753 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9754 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9755 * accept other config options):
9757 Property Type Description
9758 ---------------- --------------- ----------------------------------------------------------------------------------
9759 url String The url for the action (defaults to the form's url)
9760 method String The form method to use (defaults to the form's method, or POST if not defined)
9761 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9762 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9763 validate the form on the client (defaults to false)
9765 * @return {BasicForm} this
9767 doAction : function(action, options){
9768 if(typeof action == 'string'){
9769 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9771 if(this.fireEvent('beforeaction', this, action) !== false){
9772 this.beforeAction(action);
9773 action.run.defer(100, action);
9779 beforeAction : function(action){
9780 var o = action.options;
9785 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9787 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9790 // not really supported yet.. ??
9792 //if(this.waitMsgTarget === true){
9793 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9794 //}else if(this.waitMsgTarget){
9795 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9796 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9798 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9804 afterAction : function(action, success){
9805 this.activeAction = null;
9806 var o = action.options;
9811 Roo.get(document.body).unmask();
9817 //if(this.waitMsgTarget === true){
9818 // this.el.unmask();
9819 //}else if(this.waitMsgTarget){
9820 // this.waitMsgTarget.unmask();
9822 // Roo.MessageBox.updateProgress(1);
9823 // Roo.MessageBox.hide();
9830 Roo.callback(o.success, o.scope, [this, action]);
9831 this.fireEvent('actioncomplete', this, action);
9835 // failure condition..
9836 // we have a scenario where updates need confirming.
9837 // eg. if a locking scenario exists..
9838 // we look for { errors : { needs_confirm : true }} in the response.
9840 (typeof(action.result) != 'undefined') &&
9841 (typeof(action.result.errors) != 'undefined') &&
9842 (typeof(action.result.errors.needs_confirm) != 'undefined')
9845 Roo.log("not supported yet");
9848 Roo.MessageBox.confirm(
9849 "Change requires confirmation",
9850 action.result.errorMsg,
9855 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9865 Roo.callback(o.failure, o.scope, [this, action]);
9866 // show an error message if no failed handler is set..
9867 if (!this.hasListener('actionfailed')) {
9868 Roo.log("need to add dialog support");
9870 Roo.MessageBox.alert("Error",
9871 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9872 action.result.errorMsg :
9873 "Saving Failed, please check your entries or try again"
9878 this.fireEvent('actionfailed', this, action);
9883 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9884 * @param {String} id The value to search for
9887 findField : function(id){
9888 var items = this.getItems();
9889 var field = items.get(id);
9891 items.each(function(f){
9892 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9899 return field || null;
9902 * Mark fields in this form invalid in bulk.
9903 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9904 * @return {BasicForm} this
9906 markInvalid : function(errors){
9907 if(errors instanceof Array){
9908 for(var i = 0, len = errors.length; i < len; i++){
9909 var fieldError = errors[i];
9910 var f = this.findField(fieldError.id);
9912 f.markInvalid(fieldError.msg);
9918 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9919 field.markInvalid(errors[id]);
9923 //Roo.each(this.childForms || [], function (f) {
9924 // f.markInvalid(errors);
9931 * Set values for fields in this form in bulk.
9932 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9933 * @return {BasicForm} this
9935 setValues : function(values){
9936 if(values instanceof Array){ // array of objects
9937 for(var i = 0, len = values.length; i < len; i++){
9939 var f = this.findField(v.id);
9941 f.setValue(v.value);
9942 if(this.trackResetOnLoad){
9943 f.originalValue = f.getValue();
9947 }else{ // object hash
9950 if(typeof values[id] != 'function' && (field = this.findField(id))){
9952 if (field.setFromData &&
9954 field.displayField &&
9955 // combos' with local stores can
9956 // be queried via setValue()
9957 // to set their value..
9958 (field.store && !field.store.isLocal)
9962 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9963 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9964 field.setFromData(sd);
9966 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9968 field.setFromData(values);
9971 field.setValue(values[id]);
9975 if(this.trackResetOnLoad){
9976 field.originalValue = field.getValue();
9982 //Roo.each(this.childForms || [], function (f) {
9983 // f.setValues(values);
9990 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9991 * they are returned as an array.
9992 * @param {Boolean} asString
9995 getValues : function(asString){
9996 //if (this.childForms) {
9997 // copy values from the child forms
9998 // Roo.each(this.childForms, function (f) {
9999 // this.setValues(f.getValues());
10005 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10006 if(asString === true){
10009 return Roo.urlDecode(fs);
10013 * Returns the fields in this form as an object with key/value pairs.
10014 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10017 getFieldValues : function(with_hidden)
10019 var items = this.getItems();
10021 items.each(function(f){
10023 if (!f.getName()) {
10027 var v = f.getValue();
10029 if (f.inputType =='radio') {
10030 if (typeof(ret[f.getName()]) == 'undefined') {
10031 ret[f.getName()] = ''; // empty..
10034 if (!f.el.dom.checked) {
10038 v = f.el.dom.value;
10042 if(f.xtype == 'MoneyField'){
10043 ret[f.currencyName] = f.getCurrency();
10046 // not sure if this supported any more..
10047 if ((typeof(v) == 'object') && f.getRawValue) {
10048 v = f.getRawValue() ; // dates..
10050 // combo boxes where name != hiddenName...
10051 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10052 ret[f.name] = f.getRawValue();
10054 ret[f.getName()] = v;
10061 * Clears all invalid messages in this form.
10062 * @return {BasicForm} this
10064 clearInvalid : function(){
10065 var items = this.getItems();
10067 items.each(function(f){
10075 * Resets this form.
10076 * @return {BasicForm} this
10078 reset : function(){
10079 var items = this.getItems();
10080 items.each(function(f){
10084 Roo.each(this.childForms || [], function (f) {
10092 getItems : function()
10094 var r=new Roo.util.MixedCollection(false, function(o){
10095 return o.id || (o.id = Roo.id());
10097 var iter = function(el) {
10104 Roo.each(el.items,function(e) {
10113 hideFields : function(items)
10115 Roo.each(items, function(i){
10117 var f = this.findField(i);
10128 showFields : function(items)
10130 Roo.each(items, function(i){
10132 var f = this.findField(i);
10145 Roo.apply(Roo.bootstrap.Form, {
10161 intervalID : false,
10167 if(this.isApplied){
10172 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10173 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10174 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10175 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10178 this.maskEl.top.enableDisplayMode("block");
10179 this.maskEl.left.enableDisplayMode("block");
10180 this.maskEl.bottom.enableDisplayMode("block");
10181 this.maskEl.right.enableDisplayMode("block");
10183 this.toolTip = new Roo.bootstrap.Tooltip({
10184 cls : 'roo-form-error-popover',
10186 'left' : ['r-l', [-2,0], 'right'],
10187 'right' : ['l-r', [2,0], 'left'],
10188 'bottom' : ['tl-bl', [0,2], 'top'],
10189 'top' : [ 'bl-tl', [0,-2], 'bottom']
10193 this.toolTip.render(Roo.get(document.body));
10195 this.toolTip.el.enableDisplayMode("block");
10197 Roo.get(document.body).on('click', function(){
10201 Roo.get(document.body).on('touchstart', function(){
10205 this.isApplied = true
10208 mask : function(form, target)
10212 this.target = target;
10214 if(!this.form.errorMask || !target.el){
10218 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10220 Roo.log(scrollable);
10222 var ot = this.target.el.calcOffsetsTo(scrollable);
10224 var scrollTo = ot[1] - this.form.maskOffset;
10226 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10228 scrollable.scrollTo('top', scrollTo);
10230 var box = this.target.el.getBox();
10232 var zIndex = Roo.bootstrap.Modal.zIndex++;
10235 this.maskEl.top.setStyle('position', 'absolute');
10236 this.maskEl.top.setStyle('z-index', zIndex);
10237 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10238 this.maskEl.top.setLeft(0);
10239 this.maskEl.top.setTop(0);
10240 this.maskEl.top.show();
10242 this.maskEl.left.setStyle('position', 'absolute');
10243 this.maskEl.left.setStyle('z-index', zIndex);
10244 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10245 this.maskEl.left.setLeft(0);
10246 this.maskEl.left.setTop(box.y - this.padding);
10247 this.maskEl.left.show();
10249 this.maskEl.bottom.setStyle('position', 'absolute');
10250 this.maskEl.bottom.setStyle('z-index', zIndex);
10251 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10252 this.maskEl.bottom.setLeft(0);
10253 this.maskEl.bottom.setTop(box.bottom + this.padding);
10254 this.maskEl.bottom.show();
10256 this.maskEl.right.setStyle('position', 'absolute');
10257 this.maskEl.right.setStyle('z-index', zIndex);
10258 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10259 this.maskEl.right.setLeft(box.right + this.padding);
10260 this.maskEl.right.setTop(box.y - this.padding);
10261 this.maskEl.right.show();
10263 this.toolTip.bindEl = this.target.el;
10265 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10267 var tip = this.target.blankText;
10269 if(this.target.getValue() !== '' ) {
10271 if (this.target.invalidText.length) {
10272 tip = this.target.invalidText;
10273 } else if (this.target.regexText.length){
10274 tip = this.target.regexText;
10278 this.toolTip.show(tip);
10280 this.intervalID = window.setInterval(function() {
10281 Roo.bootstrap.Form.popover.unmask();
10284 window.onwheel = function(){ return false;};
10286 (function(){ this.isMasked = true; }).defer(500, this);
10290 unmask : function()
10292 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10296 this.maskEl.top.setStyle('position', 'absolute');
10297 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10298 this.maskEl.top.hide();
10300 this.maskEl.left.setStyle('position', 'absolute');
10301 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10302 this.maskEl.left.hide();
10304 this.maskEl.bottom.setStyle('position', 'absolute');
10305 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10306 this.maskEl.bottom.hide();
10308 this.maskEl.right.setStyle('position', 'absolute');
10309 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10310 this.maskEl.right.hide();
10312 this.toolTip.hide();
10314 this.toolTip.el.hide();
10316 window.onwheel = function(){ return true;};
10318 if(this.intervalID){
10319 window.clearInterval(this.intervalID);
10320 this.intervalID = false;
10323 this.isMasked = false;
10333 * Ext JS Library 1.1.1
10334 * Copyright(c) 2006-2007, Ext JS, LLC.
10336 * Originally Released Under LGPL - original licence link has changed is not relivant.
10339 * <script type="text/javascript">
10342 * @class Roo.form.VTypes
10343 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10346 Roo.form.VTypes = function(){
10347 // closure these in so they are only created once.
10348 var alpha = /^[a-zA-Z_]+$/;
10349 var alphanum = /^[a-zA-Z0-9_]+$/;
10350 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10351 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10353 // All these messages and functions are configurable
10356 * The function used to validate email addresses
10357 * @param {String} value The email address
10359 'email' : function(v){
10360 return email.test(v);
10363 * The error text to display when the email validation function returns false
10366 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10368 * The keystroke filter mask to be applied on email input
10371 'emailMask' : /[a-z0-9_\.\-@]/i,
10374 * The function used to validate URLs
10375 * @param {String} value The URL
10377 'url' : function(v){
10378 return url.test(v);
10381 * The error text to display when the url validation function returns false
10384 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10387 * The function used to validate alpha values
10388 * @param {String} value The value
10390 'alpha' : function(v){
10391 return alpha.test(v);
10394 * The error text to display when the alpha validation function returns false
10397 'alphaText' : 'This field should only contain letters and _',
10399 * The keystroke filter mask to be applied on alpha input
10402 'alphaMask' : /[a-z_]/i,
10405 * The function used to validate alphanumeric values
10406 * @param {String} value The value
10408 'alphanum' : function(v){
10409 return alphanum.test(v);
10412 * The error text to display when the alphanumeric validation function returns false
10415 'alphanumText' : 'This field should only contain letters, numbers and _',
10417 * The keystroke filter mask to be applied on alphanumeric input
10420 'alphanumMask' : /[a-z0-9_]/i
10430 * @class Roo.bootstrap.Input
10431 * @extends Roo.bootstrap.Component
10432 * Bootstrap Input class
10433 * @cfg {Boolean} disabled is it disabled
10434 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10435 * @cfg {String} name name of the input
10436 * @cfg {string} fieldLabel - the label associated
10437 * @cfg {string} placeholder - placeholder to put in text.
10438 * @cfg {string} before - input group add on before
10439 * @cfg {string} after - input group add on after
10440 * @cfg {string} size - (lg|sm) or leave empty..
10441 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10442 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10443 * @cfg {Number} md colspan out of 12 for computer-sized screens
10444 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10445 * @cfg {string} value default value of the input
10446 * @cfg {Number} labelWidth set the width of label
10447 * @cfg {Number} labellg set the width of label (1-12)
10448 * @cfg {Number} labelmd set the width of label (1-12)
10449 * @cfg {Number} labelsm set the width of label (1-12)
10450 * @cfg {Number} labelxs set the width of label (1-12)
10451 * @cfg {String} labelAlign (top|left)
10452 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10453 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10454 * @cfg {String} indicatorpos (left|right) default left
10455 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10456 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10457 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10459 * @cfg {String} align (left|center|right) Default left
10460 * @cfg {Boolean} forceFeedback (true|false) Default false
10463 * Create a new Input
10464 * @param {Object} config The config object
10467 Roo.bootstrap.Input = function(config){
10469 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10474 * Fires when this field receives input focus.
10475 * @param {Roo.form.Field} this
10480 * Fires when this field loses input focus.
10481 * @param {Roo.form.Field} this
10485 * @event specialkey
10486 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10487 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10488 * @param {Roo.form.Field} this
10489 * @param {Roo.EventObject} e The event object
10494 * Fires just before the field blurs if the field value has changed.
10495 * @param {Roo.form.Field} this
10496 * @param {Mixed} newValue The new value
10497 * @param {Mixed} oldValue The original value
10502 * Fires after the field has been marked as invalid.
10503 * @param {Roo.form.Field} this
10504 * @param {String} msg The validation message
10509 * Fires after the field has been validated with no errors.
10510 * @param {Roo.form.Field} this
10515 * Fires after the key up
10516 * @param {Roo.form.Field} this
10517 * @param {Roo.EventObject} e The event Object
10522 * Fires after the user pastes into input
10523 * @param {Roo.form.Field} this
10524 * @param {Roo.EventObject} e The event Object
10530 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10532 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10533 automatic validation (defaults to "keyup").
10535 validationEvent : "keyup",
10537 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10539 validateOnBlur : true,
10541 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10543 validationDelay : 250,
10545 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10547 focusClass : "x-form-focus", // not needed???
10551 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10553 invalidClass : "has-warning",
10556 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10558 validClass : "has-success",
10561 * @cfg {Boolean} hasFeedback (true|false) default true
10563 hasFeedback : true,
10566 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10568 invalidFeedbackClass : "glyphicon-warning-sign",
10571 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10573 validFeedbackClass : "glyphicon-ok",
10576 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10578 selectOnFocus : false,
10581 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10585 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10590 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10592 disableKeyFilter : false,
10595 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10599 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10603 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10605 blankText : "Please complete this mandatory field",
10608 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10612 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10614 maxLength : Number.MAX_VALUE,
10616 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10618 minLengthText : "The minimum length for this field is {0}",
10620 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10622 maxLengthText : "The maximum length for this field is {0}",
10626 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10627 * If available, this function will be called only after the basic validators all return true, and will be passed the
10628 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10632 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10633 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10634 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10638 * @cfg {String} regexText -- Depricated - use Invalid Text
10643 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10649 autocomplete: false,
10653 inputType : 'text',
10656 placeholder: false,
10661 preventMark: false,
10662 isFormField : true,
10665 labelAlign : false,
10668 formatedValue : false,
10669 forceFeedback : false,
10671 indicatorpos : 'left',
10681 parentLabelAlign : function()
10684 while (parent.parent()) {
10685 parent = parent.parent();
10686 if (typeof(parent.labelAlign) !='undefined') {
10687 return parent.labelAlign;
10694 getAutoCreate : function()
10696 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10702 if(this.inputType != 'hidden'){
10703 cfg.cls = 'form-group' //input-group
10709 type : this.inputType,
10710 value : this.value,
10711 cls : 'form-control',
10712 placeholder : this.placeholder || '',
10713 autocomplete : this.autocomplete || 'new-password'
10715 if (this.inputType == 'file') {
10716 input.style = 'overflow:hidden'; // why not in CSS?
10719 if(this.capture.length){
10720 input.capture = this.capture;
10723 if(this.accept.length){
10724 input.accept = this.accept + "/*";
10728 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10731 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10732 input.maxLength = this.maxLength;
10735 if (this.disabled) {
10736 input.disabled=true;
10739 if (this.readOnly) {
10740 input.readonly=true;
10744 input.name = this.name;
10748 input.cls += ' input-' + this.size;
10752 ['xs','sm','md','lg'].map(function(size){
10753 if (settings[size]) {
10754 cfg.cls += ' col-' + size + '-' + settings[size];
10758 var inputblock = input;
10762 cls: 'glyphicon form-control-feedback'
10765 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10768 cls : 'has-feedback',
10776 if (this.before || this.after) {
10779 cls : 'input-group',
10783 if (this.before && typeof(this.before) == 'string') {
10785 inputblock.cn.push({
10787 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10791 if (this.before && typeof(this.before) == 'object') {
10792 this.before = Roo.factory(this.before);
10794 inputblock.cn.push({
10796 cls : 'roo-input-before input-group-prepend input-group-' +
10797 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10801 inputblock.cn.push(input);
10803 if (this.after && typeof(this.after) == 'string') {
10804 inputblock.cn.push({
10806 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10810 if (this.after && typeof(this.after) == 'object') {
10811 this.after = Roo.factory(this.after);
10813 inputblock.cn.push({
10815 cls : 'roo-input-after input-group-append input-group-' +
10816 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10820 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10821 inputblock.cls += ' has-feedback';
10822 inputblock.cn.push(feedback);
10827 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10828 tooltip : 'This field is required'
10830 if (this.allowBlank ) {
10831 indicator.style = this.allowBlank ? ' display:none' : '';
10833 if (align ==='left' && this.fieldLabel.length) {
10835 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10842 cls : 'control-label col-form-label',
10843 html : this.fieldLabel
10854 var labelCfg = cfg.cn[1];
10855 var contentCfg = cfg.cn[2];
10857 if(this.indicatorpos == 'right'){
10862 cls : 'control-label col-form-label',
10866 html : this.fieldLabel
10880 labelCfg = cfg.cn[0];
10881 contentCfg = cfg.cn[1];
10885 if(this.labelWidth > 12){
10886 labelCfg.style = "width: " + this.labelWidth + 'px';
10889 if(this.labelWidth < 13 && this.labelmd == 0){
10890 this.labelmd = this.labelWidth;
10893 if(this.labellg > 0){
10894 labelCfg.cls += ' col-lg-' + this.labellg;
10895 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10898 if(this.labelmd > 0){
10899 labelCfg.cls += ' col-md-' + this.labelmd;
10900 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10903 if(this.labelsm > 0){
10904 labelCfg.cls += ' col-sm-' + this.labelsm;
10905 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10908 if(this.labelxs > 0){
10909 labelCfg.cls += ' col-xs-' + this.labelxs;
10910 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10914 } else if ( this.fieldLabel.length) {
10921 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10922 tooltip : 'This field is required',
10923 style : this.allowBlank ? ' display:none' : ''
10927 //cls : 'input-group-addon',
10928 html : this.fieldLabel
10936 if(this.indicatorpos == 'right'){
10941 //cls : 'input-group-addon',
10942 html : this.fieldLabel
10947 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10948 tooltip : 'This field is required',
10949 style : this.allowBlank ? ' display:none' : ''
10969 if (this.parentType === 'Navbar' && this.parent().bar) {
10970 cfg.cls += ' navbar-form';
10973 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10974 // on BS4 we do this only if not form
10975 cfg.cls += ' navbar-form';
10983 * return the real input element.
10985 inputEl: function ()
10987 return this.el.select('input.form-control',true).first();
10990 tooltipEl : function()
10992 return this.inputEl();
10995 indicatorEl : function()
10997 if (Roo.bootstrap.version == 4) {
10998 return false; // not enabled in v4 yet.
11001 var indicator = this.el.select('i.roo-required-indicator',true).first();
11011 setDisabled : function(v)
11013 var i = this.inputEl().dom;
11015 i.removeAttribute('disabled');
11019 i.setAttribute('disabled','true');
11021 initEvents : function()
11024 this.inputEl().on("keydown" , this.fireKey, this);
11025 this.inputEl().on("focus", this.onFocus, this);
11026 this.inputEl().on("blur", this.onBlur, this);
11028 this.inputEl().relayEvent('keyup', this);
11029 this.inputEl().relayEvent('paste', this);
11031 this.indicator = this.indicatorEl();
11033 if(this.indicator){
11034 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11037 // reference to original value for reset
11038 this.originalValue = this.getValue();
11039 //Roo.form.TextField.superclass.initEvents.call(this);
11040 if(this.validationEvent == 'keyup'){
11041 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11042 this.inputEl().on('keyup', this.filterValidation, this);
11044 else if(this.validationEvent !== false){
11045 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11048 if(this.selectOnFocus){
11049 this.on("focus", this.preFocus, this);
11052 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11053 this.inputEl().on("keypress", this.filterKeys, this);
11055 this.inputEl().relayEvent('keypress', this);
11058 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11059 this.el.on("click", this.autoSize, this);
11062 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11063 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11066 if (typeof(this.before) == 'object') {
11067 this.before.render(this.el.select('.roo-input-before',true).first());
11069 if (typeof(this.after) == 'object') {
11070 this.after.render(this.el.select('.roo-input-after',true).first());
11073 this.inputEl().on('change', this.onChange, this);
11076 filterValidation : function(e){
11077 if(!e.isNavKeyPress()){
11078 this.validationTask.delay(this.validationDelay);
11082 * Validates the field value
11083 * @return {Boolean} True if the value is valid, else false
11085 validate : function(){
11086 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11087 if(this.disabled || this.validateValue(this.getRawValue())){
11092 this.markInvalid();
11098 * Validates a value according to the field's validation rules and marks the field as invalid
11099 * if the validation fails
11100 * @param {Mixed} value The value to validate
11101 * @return {Boolean} True if the value is valid, else false
11103 validateValue : function(value)
11105 if(this.getVisibilityEl().hasClass('hidden')){
11109 if(value.length < 1) { // if it's blank
11110 if(this.allowBlank){
11116 if(value.length < this.minLength){
11119 if(value.length > this.maxLength){
11123 var vt = Roo.form.VTypes;
11124 if(!vt[this.vtype](value, this)){
11128 if(typeof this.validator == "function"){
11129 var msg = this.validator(value);
11133 if (typeof(msg) == 'string') {
11134 this.invalidText = msg;
11138 if(this.regex && !this.regex.test(value)){
11146 fireKey : function(e){
11147 //Roo.log('field ' + e.getKey());
11148 if(e.isNavKeyPress()){
11149 this.fireEvent("specialkey", this, e);
11152 focus : function (selectText){
11154 this.inputEl().focus();
11155 if(selectText === true){
11156 this.inputEl().dom.select();
11162 onFocus : function(){
11163 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11164 // this.el.addClass(this.focusClass);
11166 if(!this.hasFocus){
11167 this.hasFocus = true;
11168 this.startValue = this.getValue();
11169 this.fireEvent("focus", this);
11173 beforeBlur : Roo.emptyFn,
11177 onBlur : function(){
11179 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11180 //this.el.removeClass(this.focusClass);
11182 this.hasFocus = false;
11183 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11186 var v = this.getValue();
11187 if(String(v) !== String(this.startValue)){
11188 this.fireEvent('change', this, v, this.startValue);
11190 this.fireEvent("blur", this);
11193 onChange : function(e)
11195 var v = this.getValue();
11196 if(String(v) !== String(this.startValue)){
11197 this.fireEvent('change', this, v, this.startValue);
11203 * Resets the current field value to the originally loaded value and clears any validation messages
11205 reset : function(){
11206 this.setValue(this.originalValue);
11210 * Returns the name of the field
11211 * @return {Mixed} name The name field
11213 getName: function(){
11217 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11218 * @return {Mixed} value The field value
11220 getValue : function(){
11222 var v = this.inputEl().getValue();
11227 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11228 * @return {Mixed} value The field value
11230 getRawValue : function(){
11231 var v = this.inputEl().getValue();
11237 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11238 * @param {Mixed} value The value to set
11240 setRawValue : function(v){
11241 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11244 selectText : function(start, end){
11245 var v = this.getRawValue();
11247 start = start === undefined ? 0 : start;
11248 end = end === undefined ? v.length : end;
11249 var d = this.inputEl().dom;
11250 if(d.setSelectionRange){
11251 d.setSelectionRange(start, end);
11252 }else if(d.createTextRange){
11253 var range = d.createTextRange();
11254 range.moveStart("character", start);
11255 range.moveEnd("character", v.length-end);
11262 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11263 * @param {Mixed} value The value to set
11265 setValue : function(v){
11268 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11274 processValue : function(value){
11275 if(this.stripCharsRe){
11276 var newValue = value.replace(this.stripCharsRe, '');
11277 if(newValue !== value){
11278 this.setRawValue(newValue);
11285 preFocus : function(){
11287 if(this.selectOnFocus){
11288 this.inputEl().dom.select();
11291 filterKeys : function(e){
11292 var k = e.getKey();
11293 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11296 var c = e.getCharCode(), cc = String.fromCharCode(c);
11297 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11300 if(!this.maskRe.test(cc)){
11305 * Clear any invalid styles/messages for this field
11307 clearInvalid : function(){
11309 if(!this.el || this.preventMark){ // not rendered
11314 this.el.removeClass([this.invalidClass, 'is-invalid']);
11316 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11318 var feedback = this.el.select('.form-control-feedback', true).first();
11321 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11326 if(this.indicator){
11327 this.indicator.removeClass('visible');
11328 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11331 this.fireEvent('valid', this);
11335 * Mark this field as valid
11337 markValid : function()
11339 if(!this.el || this.preventMark){ // not rendered...
11343 this.el.removeClass([this.invalidClass, this.validClass]);
11344 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11346 var feedback = this.el.select('.form-control-feedback', true).first();
11349 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11352 if(this.indicator){
11353 this.indicator.removeClass('visible');
11354 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11362 if(this.allowBlank && !this.getRawValue().length){
11365 if (Roo.bootstrap.version == 3) {
11366 this.el.addClass(this.validClass);
11368 this.inputEl().addClass('is-valid');
11371 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11373 var feedback = this.el.select('.form-control-feedback', true).first();
11376 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11377 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11382 this.fireEvent('valid', this);
11386 * Mark this field as invalid
11387 * @param {String} msg The validation message
11389 markInvalid : function(msg)
11391 if(!this.el || this.preventMark){ // not rendered
11395 this.el.removeClass([this.invalidClass, this.validClass]);
11396 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11398 var feedback = this.el.select('.form-control-feedback', true).first();
11401 this.el.select('.form-control-feedback', true).first().removeClass(
11402 [this.invalidFeedbackClass, this.validFeedbackClass]);
11409 if(this.allowBlank && !this.getRawValue().length){
11413 if(this.indicator){
11414 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11415 this.indicator.addClass('visible');
11417 if (Roo.bootstrap.version == 3) {
11418 this.el.addClass(this.invalidClass);
11420 this.inputEl().addClass('is-invalid');
11425 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11427 var feedback = this.el.select('.form-control-feedback', true).first();
11430 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11432 if(this.getValue().length || this.forceFeedback){
11433 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11440 this.fireEvent('invalid', this, msg);
11443 SafariOnKeyDown : function(event)
11445 // this is a workaround for a password hang bug on chrome/ webkit.
11446 if (this.inputEl().dom.type != 'password') {
11450 var isSelectAll = false;
11452 if(this.inputEl().dom.selectionEnd > 0){
11453 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11455 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11456 event.preventDefault();
11461 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11463 event.preventDefault();
11464 // this is very hacky as keydown always get's upper case.
11466 var cc = String.fromCharCode(event.getCharCode());
11467 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11471 adjustWidth : function(tag, w){
11472 tag = tag.toLowerCase();
11473 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11474 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11475 if(tag == 'input'){
11478 if(tag == 'textarea'){
11481 }else if(Roo.isOpera){
11482 if(tag == 'input'){
11485 if(tag == 'textarea'){
11493 setFieldLabel : function(v)
11495 if(!this.rendered){
11499 if(this.indicatorEl()){
11500 var ar = this.el.select('label > span',true);
11502 if (ar.elements.length) {
11503 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11504 this.fieldLabel = v;
11508 var br = this.el.select('label',true);
11510 if(br.elements.length) {
11511 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11512 this.fieldLabel = v;
11516 Roo.log('Cannot Found any of label > span || label in input');
11520 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11521 this.fieldLabel = v;
11536 * @class Roo.bootstrap.TextArea
11537 * @extends Roo.bootstrap.Input
11538 * Bootstrap TextArea class
11539 * @cfg {Number} cols Specifies the visible width of a text area
11540 * @cfg {Number} rows Specifies the visible number of lines in a text area
11541 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11542 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11543 * @cfg {string} html text
11546 * Create a new TextArea
11547 * @param {Object} config The config object
11550 Roo.bootstrap.TextArea = function(config){
11551 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11555 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11565 getAutoCreate : function(){
11567 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11573 if(this.inputType != 'hidden'){
11574 cfg.cls = 'form-group' //input-group
11582 value : this.value || '',
11583 html: this.html || '',
11584 cls : 'form-control',
11585 placeholder : this.placeholder || ''
11589 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11590 input.maxLength = this.maxLength;
11594 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11598 input.cols = this.cols;
11601 if (this.readOnly) {
11602 input.readonly = true;
11606 input.name = this.name;
11610 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11614 ['xs','sm','md','lg'].map(function(size){
11615 if (settings[size]) {
11616 cfg.cls += ' col-' + size + '-' + settings[size];
11620 var inputblock = input;
11622 if(this.hasFeedback && !this.allowBlank){
11626 cls: 'glyphicon form-control-feedback'
11630 cls : 'has-feedback',
11639 if (this.before || this.after) {
11642 cls : 'input-group',
11646 inputblock.cn.push({
11648 cls : 'input-group-addon',
11653 inputblock.cn.push(input);
11655 if(this.hasFeedback && !this.allowBlank){
11656 inputblock.cls += ' has-feedback';
11657 inputblock.cn.push(feedback);
11661 inputblock.cn.push({
11663 cls : 'input-group-addon',
11670 if (align ==='left' && this.fieldLabel.length) {
11675 cls : 'control-label',
11676 html : this.fieldLabel
11687 if(this.labelWidth > 12){
11688 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11691 if(this.labelWidth < 13 && this.labelmd == 0){
11692 this.labelmd = this.labelWidth;
11695 if(this.labellg > 0){
11696 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11697 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11700 if(this.labelmd > 0){
11701 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11702 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11705 if(this.labelsm > 0){
11706 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11707 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11710 if(this.labelxs > 0){
11711 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11712 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11715 } else if ( this.fieldLabel.length) {
11720 //cls : 'input-group-addon',
11721 html : this.fieldLabel
11739 if (this.disabled) {
11740 input.disabled=true;
11747 * return the real textarea element.
11749 inputEl: function ()
11751 return this.el.select('textarea.form-control',true).first();
11755 * Clear any invalid styles/messages for this field
11757 clearInvalid : function()
11760 if(!this.el || this.preventMark){ // not rendered
11764 var label = this.el.select('label', true).first();
11765 var icon = this.el.select('i.fa-star', true).first();
11770 this.el.removeClass( this.validClass);
11771 this.inputEl().removeClass('is-invalid');
11773 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11775 var feedback = this.el.select('.form-control-feedback', true).first();
11778 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11783 this.fireEvent('valid', this);
11787 * Mark this field as valid
11789 markValid : function()
11791 if(!this.el || this.preventMark){ // not rendered
11795 this.el.removeClass([this.invalidClass, this.validClass]);
11796 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11798 var feedback = this.el.select('.form-control-feedback', true).first();
11801 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11804 if(this.disabled || this.allowBlank){
11808 var label = this.el.select('label', true).first();
11809 var icon = this.el.select('i.fa-star', true).first();
11814 if (Roo.bootstrap.version == 3) {
11815 this.el.addClass(this.validClass);
11817 this.inputEl().addClass('is-valid');
11821 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11823 var feedback = this.el.select('.form-control-feedback', true).first();
11826 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11827 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11832 this.fireEvent('valid', this);
11836 * Mark this field as invalid
11837 * @param {String} msg The validation message
11839 markInvalid : function(msg)
11841 if(!this.el || this.preventMark){ // not rendered
11845 this.el.removeClass([this.invalidClass, this.validClass]);
11846 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11848 var feedback = this.el.select('.form-control-feedback', true).first();
11851 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11854 if(this.disabled || this.allowBlank){
11858 var label = this.el.select('label', true).first();
11859 var icon = this.el.select('i.fa-star', true).first();
11861 if(!this.getValue().length && label && !icon){
11862 this.el.createChild({
11864 cls : 'text-danger fa fa-lg fa-star',
11865 tooltip : 'This field is required',
11866 style : 'margin-right:5px;'
11870 if (Roo.bootstrap.version == 3) {
11871 this.el.addClass(this.invalidClass);
11873 this.inputEl().addClass('is-invalid');
11876 // fixme ... this may be depricated need to test..
11877 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11879 var feedback = this.el.select('.form-control-feedback', true).first();
11882 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11884 if(this.getValue().length || this.forceFeedback){
11885 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11892 this.fireEvent('invalid', this, msg);
11900 * trigger field - base class for combo..
11905 * @class Roo.bootstrap.TriggerField
11906 * @extends Roo.bootstrap.Input
11907 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11908 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11909 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11910 * for which you can provide a custom implementation. For example:
11912 var trigger = new Roo.bootstrap.TriggerField();
11913 trigger.onTriggerClick = myTriggerFn;
11914 trigger.applyTo('my-field');
11917 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11918 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11919 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11920 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11921 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11924 * Create a new TriggerField.
11925 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11926 * to the base TextField)
11928 Roo.bootstrap.TriggerField = function(config){
11929 this.mimicing = false;
11930 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11933 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11935 * @cfg {String} triggerClass A CSS class to apply to the trigger
11938 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11943 * @cfg {Boolean} removable (true|false) special filter default false
11947 /** @cfg {Boolean} grow @hide */
11948 /** @cfg {Number} growMin @hide */
11949 /** @cfg {Number} growMax @hide */
11955 autoSize: Roo.emptyFn,
11959 deferHeight : true,
11962 actionMode : 'wrap',
11967 getAutoCreate : function(){
11969 var align = this.labelAlign || this.parentLabelAlign();
11974 cls: 'form-group' //input-group
11981 type : this.inputType,
11982 cls : 'form-control',
11983 autocomplete: 'new-password',
11984 placeholder : this.placeholder || ''
11988 input.name = this.name;
11991 input.cls += ' input-' + this.size;
11994 if (this.disabled) {
11995 input.disabled=true;
11998 var inputblock = input;
12000 if(this.hasFeedback && !this.allowBlank){
12004 cls: 'glyphicon form-control-feedback'
12007 if(this.removable && !this.editable ){
12009 cls : 'has-feedback',
12015 cls : 'roo-combo-removable-btn close'
12022 cls : 'has-feedback',
12031 if(this.removable && !this.editable ){
12033 cls : 'roo-removable',
12039 cls : 'roo-combo-removable-btn close'
12046 if (this.before || this.after) {
12049 cls : 'input-group',
12053 inputblock.cn.push({
12055 cls : 'input-group-addon input-group-prepend input-group-text',
12060 inputblock.cn.push(input);
12062 if(this.hasFeedback && !this.allowBlank){
12063 inputblock.cls += ' has-feedback';
12064 inputblock.cn.push(feedback);
12068 inputblock.cn.push({
12070 cls : 'input-group-addon input-group-append input-group-text',
12079 var ibwrap = inputblock;
12084 cls: 'roo-select2-choices',
12088 cls: 'roo-select2-search-field',
12100 cls: 'roo-select2-container input-group',
12105 cls: 'form-hidden-field'
12111 if(!this.multiple && this.showToggleBtn){
12117 if (this.caret != false) {
12120 cls: 'fa fa-' + this.caret
12127 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12129 Roo.bootstrap.version == 3 ? caret : '',
12132 cls: 'combobox-clear',
12146 combobox.cls += ' roo-select2-container-multi';
12150 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12151 tooltip : 'This field is required'
12153 if (Roo.bootstrap.version == 4) {
12156 style : 'display:none'
12161 if (align ==='left' && this.fieldLabel.length) {
12163 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12170 cls : 'control-label',
12171 html : this.fieldLabel
12183 var labelCfg = cfg.cn[1];
12184 var contentCfg = cfg.cn[2];
12186 if(this.indicatorpos == 'right'){
12191 cls : 'control-label',
12195 html : this.fieldLabel
12209 labelCfg = cfg.cn[0];
12210 contentCfg = cfg.cn[1];
12213 if(this.labelWidth > 12){
12214 labelCfg.style = "width: " + this.labelWidth + 'px';
12217 if(this.labelWidth < 13 && this.labelmd == 0){
12218 this.labelmd = this.labelWidth;
12221 if(this.labellg > 0){
12222 labelCfg.cls += ' col-lg-' + this.labellg;
12223 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12226 if(this.labelmd > 0){
12227 labelCfg.cls += ' col-md-' + this.labelmd;
12228 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12231 if(this.labelsm > 0){
12232 labelCfg.cls += ' col-sm-' + this.labelsm;
12233 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12236 if(this.labelxs > 0){
12237 labelCfg.cls += ' col-xs-' + this.labelxs;
12238 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12241 } else if ( this.fieldLabel.length) {
12242 // Roo.log(" label");
12247 //cls : 'input-group-addon',
12248 html : this.fieldLabel
12256 if(this.indicatorpos == 'right'){
12264 html : this.fieldLabel
12278 // Roo.log(" no label && no align");
12285 ['xs','sm','md','lg'].map(function(size){
12286 if (settings[size]) {
12287 cfg.cls += ' col-' + size + '-' + settings[size];
12298 onResize : function(w, h){
12299 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12300 // if(typeof w == 'number'){
12301 // var x = w - this.trigger.getWidth();
12302 // this.inputEl().setWidth(this.adjustWidth('input', x));
12303 // this.trigger.setStyle('left', x+'px');
12308 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12311 getResizeEl : function(){
12312 return this.inputEl();
12316 getPositionEl : function(){
12317 return this.inputEl();
12321 alignErrorIcon : function(){
12322 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12326 initEvents : function(){
12330 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12331 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12332 if(!this.multiple && this.showToggleBtn){
12333 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12334 if(this.hideTrigger){
12335 this.trigger.setDisplayed(false);
12337 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12341 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12344 if(this.removable && !this.editable && !this.tickable){
12345 var close = this.closeTriggerEl();
12348 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12349 close.on('click', this.removeBtnClick, this, close);
12353 //this.trigger.addClassOnOver('x-form-trigger-over');
12354 //this.trigger.addClassOnClick('x-form-trigger-click');
12357 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12361 closeTriggerEl : function()
12363 var close = this.el.select('.roo-combo-removable-btn', true).first();
12364 return close ? close : false;
12367 removeBtnClick : function(e, h, el)
12369 e.preventDefault();
12371 if(this.fireEvent("remove", this) !== false){
12373 this.fireEvent("afterremove", this)
12377 createList : function()
12379 this.list = Roo.get(document.body).createChild({
12380 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12381 cls: 'typeahead typeahead-long dropdown-menu shadow',
12382 style: 'display:none'
12385 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12390 initTrigger : function(){
12395 onDestroy : function(){
12397 this.trigger.removeAllListeners();
12398 // this.trigger.remove();
12401 // this.wrap.remove();
12403 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12407 onFocus : function(){
12408 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12410 if(!this.mimicing){
12411 this.wrap.addClass('x-trigger-wrap-focus');
12412 this.mimicing = true;
12413 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12414 if(this.monitorTab){
12415 this.el.on("keydown", this.checkTab, this);
12422 checkTab : function(e){
12423 if(e.getKey() == e.TAB){
12424 this.triggerBlur();
12429 onBlur : function(){
12434 mimicBlur : function(e, t){
12436 if(!this.wrap.contains(t) && this.validateBlur()){
12437 this.triggerBlur();
12443 triggerBlur : function(){
12444 this.mimicing = false;
12445 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12446 if(this.monitorTab){
12447 this.el.un("keydown", this.checkTab, this);
12449 //this.wrap.removeClass('x-trigger-wrap-focus');
12450 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12454 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12455 validateBlur : function(e, t){
12460 onDisable : function(){
12461 this.inputEl().dom.disabled = true;
12462 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12464 // this.wrap.addClass('x-item-disabled');
12469 onEnable : function(){
12470 this.inputEl().dom.disabled = false;
12471 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12473 // this.el.removeClass('x-item-disabled');
12478 onShow : function(){
12479 var ae = this.getActionEl();
12482 ae.dom.style.display = '';
12483 ae.dom.style.visibility = 'visible';
12489 onHide : function(){
12490 var ae = this.getActionEl();
12491 ae.dom.style.display = 'none';
12495 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12496 * by an implementing function.
12498 * @param {EventObject} e
12500 onTriggerClick : Roo.emptyFn
12508 * @class Roo.bootstrap.CardUploader
12509 * @extends Roo.bootstrap.Button
12510 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12511 * @cfg {Number} errorTimeout default 3000
12512 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12513 * @cfg {Array} html The button text.
12517 * Create a new CardUploader
12518 * @param {Object} config The config object
12521 Roo.bootstrap.CardUploader = function(config){
12525 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12528 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12536 * When a image is clicked on - and needs to display a slideshow or similar..
12537 * @param {Roo.bootstrap.Card} this
12538 * @param {Object} The image information data
12544 * When a the download link is clicked
12545 * @param {Roo.bootstrap.Card} this
12546 * @param {Object} The image information data contains
12553 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12556 errorTimeout : 3000,
12560 fileCollection : false,
12563 getAutoCreate : function()
12567 cls :'form-group' ,
12572 //cls : 'input-group-addon',
12573 html : this.fieldLabel
12581 value : this.value,
12582 cls : 'd-none form-control'
12587 multiple : 'multiple',
12589 cls : 'd-none roo-card-upload-selector'
12593 cls : 'roo-card-uploader-button-container w-100 mb-2'
12596 cls : 'card-columns roo-card-uploader-container'
12606 getChildContainer : function() /// what children are added to.
12608 return this.containerEl;
12611 getButtonContainer : function() /// what children are added to.
12613 return this.el.select(".roo-card-uploader-button-container").first();
12616 initEvents : function()
12619 Roo.bootstrap.Input.prototype.initEvents.call(this);
12623 xns: Roo.bootstrap,
12626 container_method : 'getButtonContainer' ,
12627 html : this.html, // fix changable?
12630 'click' : function(btn, e) {
12639 this.urlAPI = (window.createObjectURL && window) ||
12640 (window.URL && URL.revokeObjectURL && URL) ||
12641 (window.webkitURL && webkitURL);
12646 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12648 this.selectorEl.on('change', this.onFileSelected, this);
12651 this.images.forEach(function(img) {
12654 this.images = false;
12656 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12662 onClick : function(e)
12664 e.preventDefault();
12666 this.selectorEl.dom.click();
12670 onFileSelected : function(e)
12672 e.preventDefault();
12674 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12678 Roo.each(this.selectorEl.dom.files, function(file){
12679 this.addFile(file);
12688 addFile : function(file)
12691 if(typeof(file) === 'string'){
12692 throw "Add file by name?"; // should not happen
12696 if(!file || !this.urlAPI){
12706 var url = _this.urlAPI.createObjectURL( file);
12709 id : Roo.bootstrap.CardUploader.ID--,
12710 is_uploaded : false,
12714 mimetype : file.type,
12722 * addCard - add an Attachment to the uploader
12723 * @param data - the data about the image to upload
12727 title : "Title of file",
12728 is_uploaded : false,
12729 src : "http://.....",
12730 srcfile : { the File upload object },
12731 mimetype : file.type,
12734 .. any other data...
12740 addCard : function (data)
12742 // hidden input element?
12743 // if the file is not an image...
12744 //then we need to use something other that and header_image
12749 xns : Roo.bootstrap,
12750 xtype : 'CardFooter',
12753 xns : Roo.bootstrap,
12759 xns : Roo.bootstrap,
12761 html : String.format("<small>{0}</small>", data.title),
12762 cls : 'col-10 text-left',
12767 click : function() {
12769 t.fireEvent( "download", t, data );
12775 xns : Roo.bootstrap,
12777 style: 'max-height: 28px; ',
12783 click : function() {
12784 t.removeCard(data.id)
12796 var cn = this.addxtype(
12799 xns : Roo.bootstrap,
12802 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12803 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12804 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12809 initEvents : function() {
12810 Roo.bootstrap.Card.prototype.initEvents.call(this);
12812 this.imgEl = this.el.select('.card-img-top').first();
12814 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12815 this.imgEl.set({ 'pointer' : 'cursor' });
12818 this.getCardFooter().addClass('p-1');
12825 // dont' really need ot update items.
12826 // this.items.push(cn);
12827 this.fileCollection.add(cn);
12829 if (!data.srcfile) {
12830 this.updateInput();
12835 var reader = new FileReader();
12836 reader.addEventListener("load", function() {
12837 data.srcdata = reader.result;
12840 reader.readAsDataURL(data.srcfile);
12845 removeCard : function(id)
12848 var card = this.fileCollection.get(id);
12849 card.data.is_deleted = 1;
12850 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12851 //this.fileCollection.remove(card);
12852 //this.items = this.items.filter(function(e) { return e != card });
12853 // dont' really need ot update items.
12854 card.el.dom.parentNode.removeChild(card.el.dom);
12855 this.updateInput();
12861 this.fileCollection.each(function(card) {
12862 if (card.el.dom && card.el.dom.parentNode) {
12863 card.el.dom.parentNode.removeChild(card.el.dom);
12866 this.fileCollection.clear();
12867 this.updateInput();
12870 updateInput : function()
12873 this.fileCollection.each(function(e) {
12877 this.inputEl().dom.value = JSON.stringify(data);
12887 Roo.bootstrap.CardUploader.ID = -1;/*
12889 * Ext JS Library 1.1.1
12890 * Copyright(c) 2006-2007, Ext JS, LLC.
12892 * Originally Released Under LGPL - original licence link has changed is not relivant.
12895 * <script type="text/javascript">
12900 * @class Roo.data.SortTypes
12902 * Defines the default sorting (casting?) comparison functions used when sorting data.
12904 Roo.data.SortTypes = {
12906 * Default sort that does nothing
12907 * @param {Mixed} s The value being converted
12908 * @return {Mixed} The comparison value
12910 none : function(s){
12915 * The regular expression used to strip tags
12919 stripTagsRE : /<\/?[^>]+>/gi,
12922 * Strips all HTML tags to sort on text only
12923 * @param {Mixed} s The value being converted
12924 * @return {String} The comparison value
12926 asText : function(s){
12927 return String(s).replace(this.stripTagsRE, "");
12931 * Strips all HTML tags to sort on text only - Case insensitive
12932 * @param {Mixed} s The value being converted
12933 * @return {String} The comparison value
12935 asUCText : function(s){
12936 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12940 * Case insensitive string
12941 * @param {Mixed} s The value being converted
12942 * @return {String} The comparison value
12944 asUCString : function(s) {
12945 return String(s).toUpperCase();
12950 * @param {Mixed} s The value being converted
12951 * @return {Number} The comparison value
12953 asDate : function(s) {
12957 if(s instanceof Date){
12958 return s.getTime();
12960 return Date.parse(String(s));
12965 * @param {Mixed} s The value being converted
12966 * @return {Float} The comparison value
12968 asFloat : function(s) {
12969 var val = parseFloat(String(s).replace(/,/g, ""));
12978 * @param {Mixed} s The value being converted
12979 * @return {Number} The comparison value
12981 asInt : function(s) {
12982 var val = parseInt(String(s).replace(/,/g, ""));
12990 * Ext JS Library 1.1.1
12991 * Copyright(c) 2006-2007, Ext JS, LLC.
12993 * Originally Released Under LGPL - original licence link has changed is not relivant.
12996 * <script type="text/javascript">
13000 * @class Roo.data.Record
13001 * Instances of this class encapsulate both record <em>definition</em> information, and record
13002 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13003 * to access Records cached in an {@link Roo.data.Store} object.<br>
13005 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13006 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13009 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13011 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13012 * {@link #create}. The parameters are the same.
13013 * @param {Array} data An associative Array of data values keyed by the field name.
13014 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13015 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13016 * not specified an integer id is generated.
13018 Roo.data.Record = function(data, id){
13019 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13024 * Generate a constructor for a specific record layout.
13025 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13026 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13027 * Each field definition object may contain the following properties: <ul>
13028 * <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,
13029 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13030 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13031 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13032 * is being used, then this is a string containing the javascript expression to reference the data relative to
13033 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13034 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13035 * this may be omitted.</p></li>
13036 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13037 * <ul><li>auto (Default, implies no conversion)</li>
13042 * <li>date</li></ul></p></li>
13043 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13044 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13045 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13046 * by the Reader into an object that will be stored in the Record. It is passed the
13047 * following parameters:<ul>
13048 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13050 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13052 * <br>usage:<br><pre><code>
13053 var TopicRecord = Roo.data.Record.create(
13054 {name: 'title', mapping: 'topic_title'},
13055 {name: 'author', mapping: 'username'},
13056 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13057 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13058 {name: 'lastPoster', mapping: 'user2'},
13059 {name: 'excerpt', mapping: 'post_text'}
13062 var myNewRecord = new TopicRecord({
13063 title: 'Do my job please',
13066 lastPost: new Date(),
13067 lastPoster: 'Animal',
13068 excerpt: 'No way dude!'
13070 myStore.add(myNewRecord);
13075 Roo.data.Record.create = function(o){
13076 var f = function(){
13077 f.superclass.constructor.apply(this, arguments);
13079 Roo.extend(f, Roo.data.Record);
13080 var p = f.prototype;
13081 p.fields = new Roo.util.MixedCollection(false, function(field){
13084 for(var i = 0, len = o.length; i < len; i++){
13085 p.fields.add(new Roo.data.Field(o[i]));
13087 f.getField = function(name){
13088 return p.fields.get(name);
13093 Roo.data.Record.AUTO_ID = 1000;
13094 Roo.data.Record.EDIT = 'edit';
13095 Roo.data.Record.REJECT = 'reject';
13096 Roo.data.Record.COMMIT = 'commit';
13098 Roo.data.Record.prototype = {
13100 * Readonly flag - true if this record has been modified.
13109 join : function(store){
13110 this.store = store;
13114 * Set the named field to the specified value.
13115 * @param {String} name The name of the field to set.
13116 * @param {Object} value The value to set the field to.
13118 set : function(name, value){
13119 if(this.data[name] == value){
13123 if(!this.modified){
13124 this.modified = {};
13126 if(typeof this.modified[name] == 'undefined'){
13127 this.modified[name] = this.data[name];
13129 this.data[name] = value;
13130 if(!this.editing && this.store){
13131 this.store.afterEdit(this);
13136 * Get the value of the named field.
13137 * @param {String} name The name of the field to get the value of.
13138 * @return {Object} The value of the field.
13140 get : function(name){
13141 return this.data[name];
13145 beginEdit : function(){
13146 this.editing = true;
13147 this.modified = {};
13151 cancelEdit : function(){
13152 this.editing = false;
13153 delete this.modified;
13157 endEdit : function(){
13158 this.editing = false;
13159 if(this.dirty && this.store){
13160 this.store.afterEdit(this);
13165 * Usually called by the {@link Roo.data.Store} which owns the Record.
13166 * Rejects all changes made to the Record since either creation, or the last commit operation.
13167 * Modified fields are reverted to their original values.
13169 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13170 * of reject operations.
13172 reject : function(){
13173 var m = this.modified;
13175 if(typeof m[n] != "function"){
13176 this.data[n] = m[n];
13179 this.dirty = false;
13180 delete this.modified;
13181 this.editing = false;
13183 this.store.afterReject(this);
13188 * Usually called by the {@link Roo.data.Store} which owns the Record.
13189 * Commits all changes made to the Record since either creation, or the last commit operation.
13191 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13192 * of commit operations.
13194 commit : function(){
13195 this.dirty = false;
13196 delete this.modified;
13197 this.editing = false;
13199 this.store.afterCommit(this);
13204 hasError : function(){
13205 return this.error != null;
13209 clearError : function(){
13214 * Creates a copy of this record.
13215 * @param {String} id (optional) A new record id if you don't want to use this record's id
13218 copy : function(newId) {
13219 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13223 * Ext JS Library 1.1.1
13224 * Copyright(c) 2006-2007, Ext JS, LLC.
13226 * Originally Released Under LGPL - original licence link has changed is not relivant.
13229 * <script type="text/javascript">
13235 * @class Roo.data.Store
13236 * @extends Roo.util.Observable
13237 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13238 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13240 * 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
13241 * has no knowledge of the format of the data returned by the Proxy.<br>
13243 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13244 * instances from the data object. These records are cached and made available through accessor functions.
13246 * Creates a new Store.
13247 * @param {Object} config A config object containing the objects needed for the Store to access data,
13248 * and read the data into Records.
13250 Roo.data.Store = function(config){
13251 this.data = new Roo.util.MixedCollection(false);
13252 this.data.getKey = function(o){
13255 this.baseParams = {};
13257 this.paramNames = {
13262 "multisort" : "_multisort"
13265 if(config && config.data){
13266 this.inlineData = config.data;
13267 delete config.data;
13270 Roo.apply(this, config);
13272 if(this.reader){ // reader passed
13273 this.reader = Roo.factory(this.reader, Roo.data);
13274 this.reader.xmodule = this.xmodule || false;
13275 if(!this.recordType){
13276 this.recordType = this.reader.recordType;
13278 if(this.reader.onMetaChange){
13279 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13283 if(this.recordType){
13284 this.fields = this.recordType.prototype.fields;
13286 this.modified = [];
13290 * @event datachanged
13291 * Fires when the data cache has changed, and a widget which is using this Store
13292 * as a Record cache should refresh its view.
13293 * @param {Store} this
13295 datachanged : true,
13297 * @event metachange
13298 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13299 * @param {Store} this
13300 * @param {Object} meta The JSON metadata
13305 * Fires when Records have been added to the Store
13306 * @param {Store} this
13307 * @param {Roo.data.Record[]} records The array of Records added
13308 * @param {Number} index The index at which the record(s) were added
13313 * Fires when a Record has been removed from the Store
13314 * @param {Store} this
13315 * @param {Roo.data.Record} record The Record that was removed
13316 * @param {Number} index The index at which the record was removed
13321 * Fires when a Record has been updated
13322 * @param {Store} this
13323 * @param {Roo.data.Record} record The Record that was updated
13324 * @param {String} operation The update operation being performed. Value may be one of:
13326 Roo.data.Record.EDIT
13327 Roo.data.Record.REJECT
13328 Roo.data.Record.COMMIT
13334 * Fires when the data cache has been cleared.
13335 * @param {Store} this
13339 * @event beforeload
13340 * Fires before a request is made for a new data object. If the beforeload handler returns false
13341 * the load action will be canceled.
13342 * @param {Store} this
13343 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13347 * @event beforeloadadd
13348 * Fires after a new set of Records has been loaded.
13349 * @param {Store} this
13350 * @param {Roo.data.Record[]} records The Records that were loaded
13351 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13353 beforeloadadd : true,
13356 * Fires after a new set of Records has been loaded, before they are added to the store.
13357 * @param {Store} this
13358 * @param {Roo.data.Record[]} records The Records that were loaded
13359 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13360 * @params {Object} return from reader
13364 * @event loadexception
13365 * Fires if an exception occurs in the Proxy during loading.
13366 * Called with the signature of the Proxy's "loadexception" event.
13367 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13370 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13371 * @param {Object} load options
13372 * @param {Object} jsonData from your request (normally this contains the Exception)
13374 loadexception : true
13378 this.proxy = Roo.factory(this.proxy, Roo.data);
13379 this.proxy.xmodule = this.xmodule || false;
13380 this.relayEvents(this.proxy, ["loadexception"]);
13382 this.sortToggle = {};
13383 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13385 Roo.data.Store.superclass.constructor.call(this);
13387 if(this.inlineData){
13388 this.loadData(this.inlineData);
13389 delete this.inlineData;
13393 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13395 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13396 * without a remote query - used by combo/forms at present.
13400 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13403 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13406 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13407 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13410 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13411 * on any HTTP request
13414 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13417 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13421 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13422 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13424 remoteSort : false,
13427 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13428 * loaded or when a record is removed. (defaults to false).
13430 pruneModifiedRecords : false,
13433 lastOptions : null,
13436 * Add Records to the Store and fires the add event.
13437 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13439 add : function(records){
13440 records = [].concat(records);
13441 for(var i = 0, len = records.length; i < len; i++){
13442 records[i].join(this);
13444 var index = this.data.length;
13445 this.data.addAll(records);
13446 this.fireEvent("add", this, records, index);
13450 * Remove a Record from the Store and fires the remove event.
13451 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13453 remove : function(record){
13454 var index = this.data.indexOf(record);
13455 this.data.removeAt(index);
13457 if(this.pruneModifiedRecords){
13458 this.modified.remove(record);
13460 this.fireEvent("remove", this, record, index);
13464 * Remove all Records from the Store and fires the clear event.
13466 removeAll : function(){
13468 if(this.pruneModifiedRecords){
13469 this.modified = [];
13471 this.fireEvent("clear", this);
13475 * Inserts Records to the Store at the given index and fires the add event.
13476 * @param {Number} index The start index at which to insert the passed Records.
13477 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13479 insert : function(index, records){
13480 records = [].concat(records);
13481 for(var i = 0, len = records.length; i < len; i++){
13482 this.data.insert(index, records[i]);
13483 records[i].join(this);
13485 this.fireEvent("add", this, records, index);
13489 * Get the index within the cache of the passed Record.
13490 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13491 * @return {Number} The index of the passed Record. Returns -1 if not found.
13493 indexOf : function(record){
13494 return this.data.indexOf(record);
13498 * Get the index within the cache of the Record with the passed id.
13499 * @param {String} id The id of the Record to find.
13500 * @return {Number} The index of the Record. Returns -1 if not found.
13502 indexOfId : function(id){
13503 return this.data.indexOfKey(id);
13507 * Get the Record with the specified id.
13508 * @param {String} id The id of the Record to find.
13509 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13511 getById : function(id){
13512 return this.data.key(id);
13516 * Get the Record at the specified index.
13517 * @param {Number} index The index of the Record to find.
13518 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13520 getAt : function(index){
13521 return this.data.itemAt(index);
13525 * Returns a range of Records between specified indices.
13526 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13527 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13528 * @return {Roo.data.Record[]} An array of Records
13530 getRange : function(start, end){
13531 return this.data.getRange(start, end);
13535 storeOptions : function(o){
13536 o = Roo.apply({}, o);
13539 this.lastOptions = o;
13543 * Loads the Record cache from the configured Proxy using the configured Reader.
13545 * If using remote paging, then the first load call must specify the <em>start</em>
13546 * and <em>limit</em> properties in the options.params property to establish the initial
13547 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13549 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13550 * and this call will return before the new data has been loaded. Perform any post-processing
13551 * in a callback function, or in a "load" event handler.</strong>
13553 * @param {Object} options An object containing properties which control loading options:<ul>
13554 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13555 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13556 * passed the following arguments:<ul>
13557 * <li>r : Roo.data.Record[]</li>
13558 * <li>options: Options object from the load call</li>
13559 * <li>success: Boolean success indicator</li></ul></li>
13560 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13561 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13564 load : function(options){
13565 options = options || {};
13566 if(this.fireEvent("beforeload", this, options) !== false){
13567 this.storeOptions(options);
13568 var p = Roo.apply(options.params || {}, this.baseParams);
13569 // if meta was not loaded from remote source.. try requesting it.
13570 if (!this.reader.metaFromRemote) {
13571 p._requestMeta = 1;
13573 if(this.sortInfo && this.remoteSort){
13574 var pn = this.paramNames;
13575 p[pn["sort"]] = this.sortInfo.field;
13576 p[pn["dir"]] = this.sortInfo.direction;
13578 if (this.multiSort) {
13579 var pn = this.paramNames;
13580 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13583 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13588 * Reloads the Record cache from the configured Proxy using the configured Reader and
13589 * the options from the last load operation performed.
13590 * @param {Object} options (optional) An object containing properties which may override the options
13591 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13592 * the most recently used options are reused).
13594 reload : function(options){
13595 this.load(Roo.applyIf(options||{}, this.lastOptions));
13599 // Called as a callback by the Reader during a load operation.
13600 loadRecords : function(o, options, success){
13601 if(!o || success === false){
13602 if(success !== false){
13603 this.fireEvent("load", this, [], options, o);
13605 if(options.callback){
13606 options.callback.call(options.scope || this, [], options, false);
13610 // if data returned failure - throw an exception.
13611 if (o.success === false) {
13612 // show a message if no listener is registered.
13613 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13614 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13616 // loadmask wil be hooked into this..
13617 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13620 var r = o.records, t = o.totalRecords || r.length;
13622 this.fireEvent("beforeloadadd", this, r, options, o);
13624 if(!options || options.add !== true){
13625 if(this.pruneModifiedRecords){
13626 this.modified = [];
13628 for(var i = 0, len = r.length; i < len; i++){
13632 this.data = this.snapshot;
13633 delete this.snapshot;
13636 this.data.addAll(r);
13637 this.totalLength = t;
13639 this.fireEvent("datachanged", this);
13641 this.totalLength = Math.max(t, this.data.length+r.length);
13645 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13647 var e = new Roo.data.Record({});
13649 e.set(this.parent.displayField, this.parent.emptyTitle);
13650 e.set(this.parent.valueField, '');
13655 this.fireEvent("load", this, r, options, o);
13656 if(options.callback){
13657 options.callback.call(options.scope || this, r, options, true);
13663 * Loads data from a passed data block. A Reader which understands the format of the data
13664 * must have been configured in the constructor.
13665 * @param {Object} data The data block from which to read the Records. The format of the data expected
13666 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13667 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13669 loadData : function(o, append){
13670 var r = this.reader.readRecords(o);
13671 this.loadRecords(r, {add: append}, true);
13675 * using 'cn' the nested child reader read the child array into it's child stores.
13676 * @param {Object} rec The record with a 'children array
13678 loadDataFromChildren : function(rec)
13680 this.loadData(this.reader.toLoadData(rec));
13685 * Gets the number of cached records.
13687 * <em>If using paging, this may not be the total size of the dataset. If the data object
13688 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13689 * the data set size</em>
13691 getCount : function(){
13692 return this.data.length || 0;
13696 * Gets the total number of records in the dataset as returned by the server.
13698 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13699 * the dataset size</em>
13701 getTotalCount : function(){
13702 return this.totalLength || 0;
13706 * Returns the sort state of the Store as an object with two properties:
13708 field {String} The name of the field by which the Records are sorted
13709 direction {String} The sort order, "ASC" or "DESC"
13712 getSortState : function(){
13713 return this.sortInfo;
13717 applySort : function(){
13718 if(this.sortInfo && !this.remoteSort){
13719 var s = this.sortInfo, f = s.field;
13720 var st = this.fields.get(f).sortType;
13721 var fn = function(r1, r2){
13722 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13723 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13725 this.data.sort(s.direction, fn);
13726 if(this.snapshot && this.snapshot != this.data){
13727 this.snapshot.sort(s.direction, fn);
13733 * Sets the default sort column and order to be used by the next load operation.
13734 * @param {String} fieldName The name of the field to sort by.
13735 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13737 setDefaultSort : function(field, dir){
13738 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13742 * Sort the Records.
13743 * If remote sorting is used, the sort is performed on the server, and the cache is
13744 * reloaded. If local sorting is used, the cache is sorted internally.
13745 * @param {String} fieldName The name of the field to sort by.
13746 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13748 sort : function(fieldName, dir){
13749 var f = this.fields.get(fieldName);
13751 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13753 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13754 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13759 this.sortToggle[f.name] = dir;
13760 this.sortInfo = {field: f.name, direction: dir};
13761 if(!this.remoteSort){
13763 this.fireEvent("datachanged", this);
13765 this.load(this.lastOptions);
13770 * Calls the specified function for each of the Records in the cache.
13771 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13772 * Returning <em>false</em> aborts and exits the iteration.
13773 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13775 each : function(fn, scope){
13776 this.data.each(fn, scope);
13780 * Gets all records modified since the last commit. Modified records are persisted across load operations
13781 * (e.g., during paging).
13782 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13784 getModifiedRecords : function(){
13785 return this.modified;
13789 createFilterFn : function(property, value, anyMatch){
13790 if(!value.exec){ // not a regex
13791 value = String(value);
13792 if(value.length == 0){
13795 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13797 return function(r){
13798 return value.test(r.data[property]);
13803 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13804 * @param {String} property A field on your records
13805 * @param {Number} start The record index to start at (defaults to 0)
13806 * @param {Number} end The last record index to include (defaults to length - 1)
13807 * @return {Number} The sum
13809 sum : function(property, start, end){
13810 var rs = this.data.items, v = 0;
13811 start = start || 0;
13812 end = (end || end === 0) ? end : rs.length-1;
13814 for(var i = start; i <= end; i++){
13815 v += (rs[i].data[property] || 0);
13821 * Filter the records by a specified property.
13822 * @param {String} field A field on your records
13823 * @param {String/RegExp} value Either a string that the field
13824 * should start with or a RegExp to test against the field
13825 * @param {Boolean} anyMatch True to match any part not just the beginning
13827 filter : function(property, value, anyMatch){
13828 var fn = this.createFilterFn(property, value, anyMatch);
13829 return fn ? this.filterBy(fn) : this.clearFilter();
13833 * Filter by a function. The specified function will be called with each
13834 * record in this data source. If the function returns true the record is included,
13835 * otherwise it is filtered.
13836 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13837 * @param {Object} scope (optional) The scope of the function (defaults to this)
13839 filterBy : function(fn, scope){
13840 this.snapshot = this.snapshot || this.data;
13841 this.data = this.queryBy(fn, scope||this);
13842 this.fireEvent("datachanged", this);
13846 * Query the records by a specified property.
13847 * @param {String} field A field on your records
13848 * @param {String/RegExp} value Either a string that the field
13849 * should start with or a RegExp to test against the field
13850 * @param {Boolean} anyMatch True to match any part not just the beginning
13851 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13853 query : function(property, value, anyMatch){
13854 var fn = this.createFilterFn(property, value, anyMatch);
13855 return fn ? this.queryBy(fn) : this.data.clone();
13859 * Query by a function. The specified function will be called with each
13860 * record in this data source. If the function returns true the record is included
13862 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13863 * @param {Object} scope (optional) The scope of the function (defaults to this)
13864 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13866 queryBy : function(fn, scope){
13867 var data = this.snapshot || this.data;
13868 return data.filterBy(fn, scope||this);
13872 * Collects unique values for a particular dataIndex from this store.
13873 * @param {String} dataIndex The property to collect
13874 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13875 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13876 * @return {Array} An array of the unique values
13878 collect : function(dataIndex, allowNull, bypassFilter){
13879 var d = (bypassFilter === true && this.snapshot) ?
13880 this.snapshot.items : this.data.items;
13881 var v, sv, r = [], l = {};
13882 for(var i = 0, len = d.length; i < len; i++){
13883 v = d[i].data[dataIndex];
13885 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13894 * Revert to a view of the Record cache with no filtering applied.
13895 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13897 clearFilter : function(suppressEvent){
13898 if(this.snapshot && this.snapshot != this.data){
13899 this.data = this.snapshot;
13900 delete this.snapshot;
13901 if(suppressEvent !== true){
13902 this.fireEvent("datachanged", this);
13908 afterEdit : function(record){
13909 if(this.modified.indexOf(record) == -1){
13910 this.modified.push(record);
13912 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13916 afterReject : function(record){
13917 this.modified.remove(record);
13918 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13922 afterCommit : function(record){
13923 this.modified.remove(record);
13924 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13928 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13929 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13931 commitChanges : function(){
13932 var m = this.modified.slice(0);
13933 this.modified = [];
13934 for(var i = 0, len = m.length; i < len; i++){
13940 * Cancel outstanding changes on all changed records.
13942 rejectChanges : function(){
13943 var m = this.modified.slice(0);
13944 this.modified = [];
13945 for(var i = 0, len = m.length; i < len; i++){
13950 onMetaChange : function(meta, rtype, o){
13951 this.recordType = rtype;
13952 this.fields = rtype.prototype.fields;
13953 delete this.snapshot;
13954 this.sortInfo = meta.sortInfo || this.sortInfo;
13955 this.modified = [];
13956 this.fireEvent('metachange', this, this.reader.meta);
13959 moveIndex : function(data, type)
13961 var index = this.indexOf(data);
13963 var newIndex = index + type;
13967 this.insert(newIndex, data);
13972 * Ext JS Library 1.1.1
13973 * Copyright(c) 2006-2007, Ext JS, LLC.
13975 * Originally Released Under LGPL - original licence link has changed is not relivant.
13978 * <script type="text/javascript">
13982 * @class Roo.data.SimpleStore
13983 * @extends Roo.data.Store
13984 * Small helper class to make creating Stores from Array data easier.
13985 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13986 * @cfg {Array} fields An array of field definition objects, or field name strings.
13987 * @cfg {Object} an existing reader (eg. copied from another store)
13988 * @cfg {Array} data The multi-dimensional array of data
13990 * @param {Object} config
13992 Roo.data.SimpleStore = function(config)
13994 Roo.data.SimpleStore.superclass.constructor.call(this, {
13996 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13999 Roo.data.Record.create(config.fields)
14001 proxy : new Roo.data.MemoryProxy(config.data)
14005 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14007 * Ext JS Library 1.1.1
14008 * Copyright(c) 2006-2007, Ext JS, LLC.
14010 * Originally Released Under LGPL - original licence link has changed is not relivant.
14013 * <script type="text/javascript">
14018 * @extends Roo.data.Store
14019 * @class Roo.data.JsonStore
14020 * Small helper class to make creating Stores for JSON data easier. <br/>
14022 var store = new Roo.data.JsonStore({
14023 url: 'get-images.php',
14025 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14028 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14029 * JsonReader and HttpProxy (unless inline data is provided).</b>
14030 * @cfg {Array} fields An array of field definition objects, or field name strings.
14032 * @param {Object} config
14034 Roo.data.JsonStore = function(c){
14035 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14036 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14037 reader: new Roo.data.JsonReader(c, c.fields)
14040 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14042 * Ext JS Library 1.1.1
14043 * Copyright(c) 2006-2007, Ext JS, LLC.
14045 * Originally Released Under LGPL - original licence link has changed is not relivant.
14048 * <script type="text/javascript">
14052 Roo.data.Field = function(config){
14053 if(typeof config == "string"){
14054 config = {name: config};
14056 Roo.apply(this, config);
14059 this.type = "auto";
14062 var st = Roo.data.SortTypes;
14063 // named sortTypes are supported, here we look them up
14064 if(typeof this.sortType == "string"){
14065 this.sortType = st[this.sortType];
14068 // set default sortType for strings and dates
14069 if(!this.sortType){
14072 this.sortType = st.asUCString;
14075 this.sortType = st.asDate;
14078 this.sortType = st.none;
14083 var stripRe = /[\$,%]/g;
14085 // prebuilt conversion function for this field, instead of
14086 // switching every time we're reading a value
14088 var cv, dateFormat = this.dateFormat;
14093 cv = function(v){ return v; };
14096 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14100 return v !== undefined && v !== null && v !== '' ?
14101 parseInt(String(v).replace(stripRe, ""), 10) : '';
14106 return v !== undefined && v !== null && v !== '' ?
14107 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14112 cv = function(v){ return v === true || v === "true" || v == 1; };
14119 if(v instanceof Date){
14123 if(dateFormat == "timestamp"){
14124 return new Date(v*1000);
14126 return Date.parseDate(v, dateFormat);
14128 var parsed = Date.parse(v);
14129 return parsed ? new Date(parsed) : null;
14138 Roo.data.Field.prototype = {
14146 * Ext JS Library 1.1.1
14147 * Copyright(c) 2006-2007, Ext JS, LLC.
14149 * Originally Released Under LGPL - original licence link has changed is not relivant.
14152 * <script type="text/javascript">
14155 // Base class for reading structured data from a data source. This class is intended to be
14156 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14159 * @class Roo.data.DataReader
14160 * Base class for reading structured data from a data source. This class is intended to be
14161 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14164 Roo.data.DataReader = function(meta, recordType){
14168 this.recordType = recordType instanceof Array ?
14169 Roo.data.Record.create(recordType) : recordType;
14172 Roo.data.DataReader.prototype = {
14175 readerType : 'Data',
14177 * Create an empty record
14178 * @param {Object} data (optional) - overlay some values
14179 * @return {Roo.data.Record} record created.
14181 newRow : function(d) {
14183 this.recordType.prototype.fields.each(function(c) {
14185 case 'int' : da[c.name] = 0; break;
14186 case 'date' : da[c.name] = new Date(); break;
14187 case 'float' : da[c.name] = 0.0; break;
14188 case 'boolean' : da[c.name] = false; break;
14189 default : da[c.name] = ""; break;
14193 return new this.recordType(Roo.apply(da, d));
14199 * Ext JS Library 1.1.1
14200 * Copyright(c) 2006-2007, Ext JS, LLC.
14202 * Originally Released Under LGPL - original licence link has changed is not relivant.
14205 * <script type="text/javascript">
14209 * @class Roo.data.DataProxy
14210 * @extends Roo.data.Observable
14211 * This class is an abstract base class for implementations which provide retrieval of
14212 * unformatted data objects.<br>
14214 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14215 * (of the appropriate type which knows how to parse the data object) to provide a block of
14216 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14218 * Custom implementations must implement the load method as described in
14219 * {@link Roo.data.HttpProxy#load}.
14221 Roo.data.DataProxy = function(){
14224 * @event beforeload
14225 * Fires before a network request is made to retrieve a data object.
14226 * @param {Object} This DataProxy object.
14227 * @param {Object} params The params parameter to the load function.
14232 * Fires before the load method's callback is called.
14233 * @param {Object} This DataProxy object.
14234 * @param {Object} o The data object.
14235 * @param {Object} arg The callback argument object passed to the load function.
14239 * @event loadexception
14240 * Fires if an Exception occurs during data retrieval.
14241 * @param {Object} This DataProxy object.
14242 * @param {Object} o The data object.
14243 * @param {Object} arg The callback argument object passed to the load function.
14244 * @param {Object} e The Exception.
14246 loadexception : true
14248 Roo.data.DataProxy.superclass.constructor.call(this);
14251 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14254 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14258 * Ext JS Library 1.1.1
14259 * Copyright(c) 2006-2007, Ext JS, LLC.
14261 * Originally Released Under LGPL - original licence link has changed is not relivant.
14264 * <script type="text/javascript">
14267 * @class Roo.data.MemoryProxy
14268 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14269 * to the Reader when its load method is called.
14271 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14273 Roo.data.MemoryProxy = function(data){
14277 Roo.data.MemoryProxy.superclass.constructor.call(this);
14281 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14284 * Load data from the requested source (in this case an in-memory
14285 * data object passed to the constructor), read the data object into
14286 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14287 * process that block using the passed callback.
14288 * @param {Object} params This parameter is not used by the MemoryProxy class.
14289 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14290 * object into a block of Roo.data.Records.
14291 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14292 * The function must be passed <ul>
14293 * <li>The Record block object</li>
14294 * <li>The "arg" argument from the load function</li>
14295 * <li>A boolean success indicator</li>
14297 * @param {Object} scope The scope in which to call the callback
14298 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14300 load : function(params, reader, callback, scope, arg){
14301 params = params || {};
14304 result = reader.readRecords(params.data ? params.data :this.data);
14306 this.fireEvent("loadexception", this, arg, null, e);
14307 callback.call(scope, null, arg, false);
14310 callback.call(scope, result, arg, true);
14314 update : function(params, records){
14319 * Ext JS Library 1.1.1
14320 * Copyright(c) 2006-2007, Ext JS, LLC.
14322 * Originally Released Under LGPL - original licence link has changed is not relivant.
14325 * <script type="text/javascript">
14328 * @class Roo.data.HttpProxy
14329 * @extends Roo.data.DataProxy
14330 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14331 * configured to reference a certain URL.<br><br>
14333 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14334 * from which the running page was served.<br><br>
14336 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14338 * Be aware that to enable the browser to parse an XML document, the server must set
14339 * the Content-Type header in the HTTP response to "text/xml".
14341 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14342 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14343 * will be used to make the request.
14345 Roo.data.HttpProxy = function(conn){
14346 Roo.data.HttpProxy.superclass.constructor.call(this);
14347 // is conn a conn config or a real conn?
14349 this.useAjax = !conn || !conn.events;
14353 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14354 // thse are take from connection...
14357 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14360 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14361 * extra parameters to each request made by this object. (defaults to undefined)
14364 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14365 * to each request made by this object. (defaults to undefined)
14368 * @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)
14371 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14374 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14380 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14384 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14385 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14386 * a finer-grained basis than the DataProxy events.
14388 getConnection : function(){
14389 return this.useAjax ? Roo.Ajax : this.conn;
14393 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14394 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14395 * process that block using the passed callback.
14396 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14397 * for the request to the remote server.
14398 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14399 * object into a block of Roo.data.Records.
14400 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14401 * The function must be passed <ul>
14402 * <li>The Record block object</li>
14403 * <li>The "arg" argument from the load function</li>
14404 * <li>A boolean success indicator</li>
14406 * @param {Object} scope The scope in which to call the callback
14407 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14409 load : function(params, reader, callback, scope, arg){
14410 if(this.fireEvent("beforeload", this, params) !== false){
14412 params : params || {},
14414 callback : callback,
14419 callback : this.loadResponse,
14423 Roo.applyIf(o, this.conn);
14424 if(this.activeRequest){
14425 Roo.Ajax.abort(this.activeRequest);
14427 this.activeRequest = Roo.Ajax.request(o);
14429 this.conn.request(o);
14432 callback.call(scope||this, null, arg, false);
14437 loadResponse : function(o, success, response){
14438 delete this.activeRequest;
14440 this.fireEvent("loadexception", this, o, response);
14441 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14446 result = o.reader.read(response);
14448 this.fireEvent("loadexception", this, o, response, e);
14449 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14453 this.fireEvent("load", this, o, o.request.arg);
14454 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14458 update : function(dataSet){
14463 updateResponse : function(dataSet){
14468 * Ext JS Library 1.1.1
14469 * Copyright(c) 2006-2007, Ext JS, LLC.
14471 * Originally Released Under LGPL - original licence link has changed is not relivant.
14474 * <script type="text/javascript">
14478 * @class Roo.data.ScriptTagProxy
14479 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14480 * other than the originating domain of the running page.<br><br>
14482 * <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
14483 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14485 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14486 * source code that is used as the source inside a <script> tag.<br><br>
14488 * In order for the browser to process the returned data, the server must wrap the data object
14489 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14490 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14491 * depending on whether the callback name was passed:
14494 boolean scriptTag = false;
14495 String cb = request.getParameter("callback");
14498 response.setContentType("text/javascript");
14500 response.setContentType("application/x-json");
14502 Writer out = response.getWriter();
14504 out.write(cb + "(");
14506 out.print(dataBlock.toJsonString());
14513 * @param {Object} config A configuration object.
14515 Roo.data.ScriptTagProxy = function(config){
14516 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14517 Roo.apply(this, config);
14518 this.head = document.getElementsByTagName("head")[0];
14521 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14523 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14525 * @cfg {String} url The URL from which to request the data object.
14528 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14532 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14533 * the server the name of the callback function set up by the load call to process the returned data object.
14534 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14535 * javascript output which calls this named function passing the data object as its only parameter.
14537 callbackParam : "callback",
14539 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14540 * name to the request.
14545 * Load data from the configured URL, read the data object into
14546 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14547 * process that block using the passed callback.
14548 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14549 * for the request to the remote server.
14550 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14551 * object into a block of Roo.data.Records.
14552 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14553 * The function must be passed <ul>
14554 * <li>The Record block object</li>
14555 * <li>The "arg" argument from the load function</li>
14556 * <li>A boolean success indicator</li>
14558 * @param {Object} scope The scope in which to call the callback
14559 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14561 load : function(params, reader, callback, scope, arg){
14562 if(this.fireEvent("beforeload", this, params) !== false){
14564 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14566 var url = this.url;
14567 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14569 url += "&_dc=" + (new Date().getTime());
14571 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14574 cb : "stcCallback"+transId,
14575 scriptId : "stcScript"+transId,
14579 callback : callback,
14585 window[trans.cb] = function(o){
14586 conn.handleResponse(o, trans);
14589 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14591 if(this.autoAbort !== false){
14595 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14597 var script = document.createElement("script");
14598 script.setAttribute("src", url);
14599 script.setAttribute("type", "text/javascript");
14600 script.setAttribute("id", trans.scriptId);
14601 this.head.appendChild(script);
14603 this.trans = trans;
14605 callback.call(scope||this, null, arg, false);
14610 isLoading : function(){
14611 return this.trans ? true : false;
14615 * Abort the current server request.
14617 abort : function(){
14618 if(this.isLoading()){
14619 this.destroyTrans(this.trans);
14624 destroyTrans : function(trans, isLoaded){
14625 this.head.removeChild(document.getElementById(trans.scriptId));
14626 clearTimeout(trans.timeoutId);
14628 window[trans.cb] = undefined;
14630 delete window[trans.cb];
14633 // if hasn't been loaded, wait for load to remove it to prevent script error
14634 window[trans.cb] = function(){
14635 window[trans.cb] = undefined;
14637 delete window[trans.cb];
14644 handleResponse : function(o, trans){
14645 this.trans = false;
14646 this.destroyTrans(trans, true);
14649 result = trans.reader.readRecords(o);
14651 this.fireEvent("loadexception", this, o, trans.arg, e);
14652 trans.callback.call(trans.scope||window, null, trans.arg, false);
14655 this.fireEvent("load", this, o, trans.arg);
14656 trans.callback.call(trans.scope||window, result, trans.arg, true);
14660 handleFailure : function(trans){
14661 this.trans = false;
14662 this.destroyTrans(trans, false);
14663 this.fireEvent("loadexception", this, null, trans.arg);
14664 trans.callback.call(trans.scope||window, null, trans.arg, false);
14668 * Ext JS Library 1.1.1
14669 * Copyright(c) 2006-2007, Ext JS, LLC.
14671 * Originally Released Under LGPL - original licence link has changed is not relivant.
14674 * <script type="text/javascript">
14678 * @class Roo.data.JsonReader
14679 * @extends Roo.data.DataReader
14680 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14681 * based on mappings in a provided Roo.data.Record constructor.
14683 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14684 * in the reply previously.
14689 var RecordDef = Roo.data.Record.create([
14690 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14691 {name: 'occupation'} // This field will use "occupation" as the mapping.
14693 var myReader = new Roo.data.JsonReader({
14694 totalProperty: "results", // The property which contains the total dataset size (optional)
14695 root: "rows", // The property which contains an Array of row objects
14696 id: "id" // The property within each row object that provides an ID for the record (optional)
14700 * This would consume a JSON file like this:
14702 { 'results': 2, 'rows': [
14703 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14704 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14707 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14708 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14709 * paged from the remote server.
14710 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14711 * @cfg {String} root name of the property which contains the Array of row objects.
14712 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14713 * @cfg {Array} fields Array of field definition objects
14715 * Create a new JsonReader
14716 * @param {Object} meta Metadata configuration options
14717 * @param {Object} recordType Either an Array of field definition objects,
14718 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14720 Roo.data.JsonReader = function(meta, recordType){
14723 // set some defaults:
14724 Roo.applyIf(meta, {
14725 totalProperty: 'total',
14726 successProperty : 'success',
14731 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14733 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14735 readerType : 'Json',
14738 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14739 * Used by Store query builder to append _requestMeta to params.
14742 metaFromRemote : false,
14744 * This method is only used by a DataProxy which has retrieved data from a remote server.
14745 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14746 * @return {Object} data A data block which is used by an Roo.data.Store object as
14747 * a cache of Roo.data.Records.
14749 read : function(response){
14750 var json = response.responseText;
14752 var o = /* eval:var:o */ eval("("+json+")");
14754 throw {message: "JsonReader.read: Json object not found"};
14760 this.metaFromRemote = true;
14761 this.meta = o.metaData;
14762 this.recordType = Roo.data.Record.create(o.metaData.fields);
14763 this.onMetaChange(this.meta, this.recordType, o);
14765 return this.readRecords(o);
14768 // private function a store will implement
14769 onMetaChange : function(meta, recordType, o){
14776 simpleAccess: function(obj, subsc) {
14783 getJsonAccessor: function(){
14785 return function(expr) {
14787 return(re.test(expr))
14788 ? new Function("obj", "return obj." + expr)
14793 return Roo.emptyFn;
14798 * Create a data block containing Roo.data.Records from an XML document.
14799 * @param {Object} o An object which contains an Array of row objects in the property specified
14800 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14801 * which contains the total size of the dataset.
14802 * @return {Object} data A data block which is used by an Roo.data.Store object as
14803 * a cache of Roo.data.Records.
14805 readRecords : function(o){
14807 * After any data loads, the raw JSON data is available for further custom processing.
14811 var s = this.meta, Record = this.recordType,
14812 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14814 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14816 if(s.totalProperty) {
14817 this.getTotal = this.getJsonAccessor(s.totalProperty);
14819 if(s.successProperty) {
14820 this.getSuccess = this.getJsonAccessor(s.successProperty);
14822 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14824 var g = this.getJsonAccessor(s.id);
14825 this.getId = function(rec) {
14827 return (r === undefined || r === "") ? null : r;
14830 this.getId = function(){return null;};
14833 for(var jj = 0; jj < fl; jj++){
14835 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14836 this.ef[jj] = this.getJsonAccessor(map);
14840 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14841 if(s.totalProperty){
14842 var vt = parseInt(this.getTotal(o), 10);
14847 if(s.successProperty){
14848 var vs = this.getSuccess(o);
14849 if(vs === false || vs === 'false'){
14854 for(var i = 0; i < c; i++){
14857 var id = this.getId(n);
14858 for(var j = 0; j < fl; j++){
14860 var v = this.ef[j](n);
14862 Roo.log('missing convert for ' + f.name);
14866 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14868 var record = new Record(values, id);
14870 records[i] = record;
14876 totalRecords : totalRecords
14879 // used when loading children.. @see loadDataFromChildren
14880 toLoadData: function(rec)
14882 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14883 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14884 return { data : data, total : data.length };
14889 * Ext JS Library 1.1.1
14890 * Copyright(c) 2006-2007, Ext JS, LLC.
14892 * Originally Released Under LGPL - original licence link has changed is not relivant.
14895 * <script type="text/javascript">
14899 * @class Roo.data.ArrayReader
14900 * @extends Roo.data.DataReader
14901 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14902 * Each element of that Array represents a row of data fields. The
14903 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14904 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14908 var RecordDef = Roo.data.Record.create([
14909 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14910 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14912 var myReader = new Roo.data.ArrayReader({
14913 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14917 * This would consume an Array like this:
14919 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14923 * Create a new JsonReader
14924 * @param {Object} meta Metadata configuration options.
14925 * @param {Object|Array} recordType Either an Array of field definition objects
14927 * @cfg {Array} fields Array of field definition objects
14928 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14929 * as specified to {@link Roo.data.Record#create},
14930 * or an {@link Roo.data.Record} object
14933 * created using {@link Roo.data.Record#create}.
14935 Roo.data.ArrayReader = function(meta, recordType)
14937 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14940 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14943 * Create a data block containing Roo.data.Records from an XML document.
14944 * @param {Object} o An Array of row objects which represents the dataset.
14945 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14946 * a cache of Roo.data.Records.
14948 readRecords : function(o)
14950 var sid = this.meta ? this.meta.id : null;
14951 var recordType = this.recordType, fields = recordType.prototype.fields;
14954 for(var i = 0; i < root.length; i++){
14957 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14958 for(var j = 0, jlen = fields.length; j < jlen; j++){
14959 var f = fields.items[j];
14960 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14961 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14963 values[f.name] = v;
14965 var record = new recordType(values, id);
14967 records[records.length] = record;
14971 totalRecords : records.length
14974 // used when loading children.. @see loadDataFromChildren
14975 toLoadData: function(rec)
14977 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14978 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14989 * @class Roo.bootstrap.ComboBox
14990 * @extends Roo.bootstrap.TriggerField
14991 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14992 * @cfg {Boolean} append (true|false) default false
14993 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14994 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14995 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14996 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14997 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14998 * @cfg {Boolean} animate default true
14999 * @cfg {Boolean} emptyResultText only for touch device
15000 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15001 * @cfg {String} emptyTitle default ''
15002 * @cfg {Number} width fixed with? experimental
15004 * Create a new ComboBox.
15005 * @param {Object} config Configuration options
15007 Roo.bootstrap.ComboBox = function(config){
15008 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15012 * Fires when the dropdown list is expanded
15013 * @param {Roo.bootstrap.ComboBox} combo This combo box
15018 * Fires when the dropdown list is collapsed
15019 * @param {Roo.bootstrap.ComboBox} combo This combo box
15023 * @event beforeselect
15024 * Fires before a list item is selected. Return false to cancel the selection.
15025 * @param {Roo.bootstrap.ComboBox} combo This combo box
15026 * @param {Roo.data.Record} record The data record returned from the underlying store
15027 * @param {Number} index The index of the selected item in the dropdown list
15029 'beforeselect' : true,
15032 * Fires when a list item is selected
15033 * @param {Roo.bootstrap.ComboBox} combo This combo box
15034 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15035 * @param {Number} index The index of the selected item in the dropdown list
15039 * @event beforequery
15040 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15041 * The event object passed has these properties:
15042 * @param {Roo.bootstrap.ComboBox} combo This combo box
15043 * @param {String} query The query
15044 * @param {Boolean} forceAll true to force "all" query
15045 * @param {Boolean} cancel true to cancel the query
15046 * @param {Object} e The query event object
15048 'beforequery': true,
15051 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15052 * @param {Roo.bootstrap.ComboBox} combo This combo box
15057 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15058 * @param {Roo.bootstrap.ComboBox} combo This combo box
15059 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15064 * Fires when the remove value from the combobox array
15065 * @param {Roo.bootstrap.ComboBox} combo This combo box
15069 * @event afterremove
15070 * Fires when the remove value from the combobox array
15071 * @param {Roo.bootstrap.ComboBox} combo This combo box
15073 'afterremove' : true,
15075 * @event specialfilter
15076 * Fires when specialfilter
15077 * @param {Roo.bootstrap.ComboBox} combo This combo box
15079 'specialfilter' : true,
15082 * Fires when tick the element
15083 * @param {Roo.bootstrap.ComboBox} combo This combo box
15087 * @event touchviewdisplay
15088 * Fires when touch view require special display (default is using displayField)
15089 * @param {Roo.bootstrap.ComboBox} combo This combo box
15090 * @param {Object} cfg set html .
15092 'touchviewdisplay' : true
15097 this.tickItems = [];
15099 this.selectedIndex = -1;
15100 if(this.mode == 'local'){
15101 if(config.queryDelay === undefined){
15102 this.queryDelay = 10;
15104 if(config.minChars === undefined){
15110 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15113 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15114 * rendering into an Roo.Editor, defaults to false)
15117 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15118 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15121 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15124 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15125 * the dropdown list (defaults to undefined, with no header element)
15129 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15133 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15135 listWidth: undefined,
15137 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15138 * mode = 'remote' or 'text' if mode = 'local')
15140 displayField: undefined,
15143 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15144 * mode = 'remote' or 'value' if mode = 'local').
15145 * Note: use of a valueField requires the user make a selection
15146 * in order for a value to be mapped.
15148 valueField: undefined,
15150 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15155 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15156 * field's data value (defaults to the underlying DOM element's name)
15158 hiddenName: undefined,
15160 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15164 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15166 selectedClass: 'active',
15169 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15173 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15174 * anchor positions (defaults to 'tl-bl')
15176 listAlign: 'tl-bl?',
15178 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15182 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15183 * query specified by the allQuery config option (defaults to 'query')
15185 triggerAction: 'query',
15187 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15188 * (defaults to 4, does not apply if editable = false)
15192 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15193 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15197 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15198 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15202 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15203 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15207 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15208 * when editable = true (defaults to false)
15210 selectOnFocus:false,
15212 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15214 queryParam: 'query',
15216 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15217 * when mode = 'remote' (defaults to 'Loading...')
15219 loadingText: 'Loading...',
15221 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15225 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15229 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15230 * traditional select (defaults to true)
15234 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15238 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15242 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15243 * listWidth has a higher value)
15247 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15248 * allow the user to set arbitrary text into the field (defaults to false)
15250 forceSelection:false,
15252 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15253 * if typeAhead = true (defaults to 250)
15255 typeAheadDelay : 250,
15257 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15258 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15260 valueNotFoundText : undefined,
15262 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15264 blockFocus : false,
15267 * @cfg {Boolean} disableClear Disable showing of clear button.
15269 disableClear : false,
15271 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15273 alwaysQuery : false,
15276 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15281 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15283 invalidClass : "has-warning",
15286 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15288 validClass : "has-success",
15291 * @cfg {Boolean} specialFilter (true|false) special filter default false
15293 specialFilter : false,
15296 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15298 mobileTouchView : true,
15301 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15303 useNativeIOS : false,
15306 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15308 mobile_restrict_height : false,
15310 ios_options : false,
15322 btnPosition : 'right',
15323 triggerList : true,
15324 showToggleBtn : true,
15326 emptyResultText: 'Empty',
15327 triggerText : 'Select',
15331 // element that contains real text value.. (when hidden is used..)
15333 getAutoCreate : function()
15338 * Render classic select for iso
15341 if(Roo.isIOS && this.useNativeIOS){
15342 cfg = this.getAutoCreateNativeIOS();
15350 if(Roo.isTouch && this.mobileTouchView){
15351 cfg = this.getAutoCreateTouchView();
15358 if(!this.tickable){
15359 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15364 * ComboBox with tickable selections
15367 var align = this.labelAlign || this.parentLabelAlign();
15370 cls : 'form-group roo-combobox-tickable' //input-group
15373 var btn_text_select = '';
15374 var btn_text_done = '';
15375 var btn_text_cancel = '';
15377 if (this.btn_text_show) {
15378 btn_text_select = 'Select';
15379 btn_text_done = 'Done';
15380 btn_text_cancel = 'Cancel';
15385 cls : 'tickable-buttons',
15390 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15391 //html : this.triggerText
15392 html: btn_text_select
15398 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15400 html: btn_text_done
15406 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15408 html: btn_text_cancel
15414 buttons.cn.unshift({
15416 cls: 'roo-select2-search-field-input'
15422 Roo.each(buttons.cn, function(c){
15424 c.cls += ' btn-' + _this.size;
15427 if (_this.disabled) {
15434 style : 'display: contents',
15439 cls: 'form-hidden-field'
15443 cls: 'roo-select2-choices',
15447 cls: 'roo-select2-search-field',
15458 cls: 'roo-select2-container input-group roo-select2-container-multi',
15464 // cls: 'typeahead typeahead-long dropdown-menu',
15465 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15470 if(this.hasFeedback && !this.allowBlank){
15474 cls: 'glyphicon form-control-feedback'
15477 combobox.cn.push(feedback);
15484 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15485 tooltip : 'This field is required'
15487 if (Roo.bootstrap.version == 4) {
15490 style : 'display:none'
15493 if (align ==='left' && this.fieldLabel.length) {
15495 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15502 cls : 'control-label col-form-label',
15503 html : this.fieldLabel
15515 var labelCfg = cfg.cn[1];
15516 var contentCfg = cfg.cn[2];
15519 if(this.indicatorpos == 'right'){
15525 cls : 'control-label col-form-label',
15529 html : this.fieldLabel
15545 labelCfg = cfg.cn[0];
15546 contentCfg = cfg.cn[1];
15550 if(this.labelWidth > 12){
15551 labelCfg.style = "width: " + this.labelWidth + 'px';
15553 if(this.width * 1 > 0){
15554 contentCfg.style = "width: " + this.width + 'px';
15556 if(this.labelWidth < 13 && this.labelmd == 0){
15557 this.labelmd = this.labelWidth;
15560 if(this.labellg > 0){
15561 labelCfg.cls += ' col-lg-' + this.labellg;
15562 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15565 if(this.labelmd > 0){
15566 labelCfg.cls += ' col-md-' + this.labelmd;
15567 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15570 if(this.labelsm > 0){
15571 labelCfg.cls += ' col-sm-' + this.labelsm;
15572 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15575 if(this.labelxs > 0){
15576 labelCfg.cls += ' col-xs-' + this.labelxs;
15577 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15581 } else if ( this.fieldLabel.length) {
15582 // Roo.log(" label");
15587 //cls : 'input-group-addon',
15588 html : this.fieldLabel
15593 if(this.indicatorpos == 'right'){
15597 //cls : 'input-group-addon',
15598 html : this.fieldLabel
15608 // Roo.log(" no label && no align");
15615 ['xs','sm','md','lg'].map(function(size){
15616 if (settings[size]) {
15617 cfg.cls += ' col-' + size + '-' + settings[size];
15625 _initEventsCalled : false,
15628 initEvents: function()
15630 if (this._initEventsCalled) { // as we call render... prevent looping...
15633 this._initEventsCalled = true;
15636 throw "can not find store for combo";
15639 this.indicator = this.indicatorEl();
15641 this.store = Roo.factory(this.store, Roo.data);
15642 this.store.parent = this;
15644 // if we are building from html. then this element is so complex, that we can not really
15645 // use the rendered HTML.
15646 // so we have to trash and replace the previous code.
15647 if (Roo.XComponent.build_from_html) {
15648 // remove this element....
15649 var e = this.el.dom, k=0;
15650 while (e ) { e = e.previousSibling; ++k;}
15655 this.rendered = false;
15657 this.render(this.parent().getChildContainer(true), k);
15660 if(Roo.isIOS && this.useNativeIOS){
15661 this.initIOSView();
15669 if(Roo.isTouch && this.mobileTouchView){
15670 this.initTouchView();
15675 this.initTickableEvents();
15679 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15681 if(this.hiddenName){
15683 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15685 this.hiddenField.dom.value =
15686 this.hiddenValue !== undefined ? this.hiddenValue :
15687 this.value !== undefined ? this.value : '';
15689 // prevent input submission
15690 this.el.dom.removeAttribute('name');
15691 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15696 // this.el.dom.setAttribute('autocomplete', 'off');
15699 var cls = 'x-combo-list';
15701 //this.list = new Roo.Layer({
15702 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15708 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15709 _this.list.setWidth(lw);
15712 this.list.on('mouseover', this.onViewOver, this);
15713 this.list.on('mousemove', this.onViewMove, this);
15714 this.list.on('scroll', this.onViewScroll, this);
15717 this.list.swallowEvent('mousewheel');
15718 this.assetHeight = 0;
15721 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15722 this.assetHeight += this.header.getHeight();
15725 this.innerList = this.list.createChild({cls:cls+'-inner'});
15726 this.innerList.on('mouseover', this.onViewOver, this);
15727 this.innerList.on('mousemove', this.onViewMove, this);
15728 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15730 if(this.allowBlank && !this.pageSize && !this.disableClear){
15731 this.footer = this.list.createChild({cls:cls+'-ft'});
15732 this.pageTb = new Roo.Toolbar(this.footer);
15736 this.footer = this.list.createChild({cls:cls+'-ft'});
15737 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15738 {pageSize: this.pageSize});
15742 if (this.pageTb && this.allowBlank && !this.disableClear) {
15744 this.pageTb.add(new Roo.Toolbar.Fill(), {
15745 cls: 'x-btn-icon x-btn-clear',
15747 handler: function()
15750 _this.clearValue();
15751 _this.onSelect(false, -1);
15756 this.assetHeight += this.footer.getHeight();
15761 this.tpl = Roo.bootstrap.version == 4 ?
15762 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15763 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15766 this.view = new Roo.View(this.list, this.tpl, {
15767 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15769 //this.view.wrapEl.setDisplayed(false);
15770 this.view.on('click', this.onViewClick, this);
15773 this.store.on('beforeload', this.onBeforeLoad, this);
15774 this.store.on('load', this.onLoad, this);
15775 this.store.on('loadexception', this.onLoadException, this);
15777 if(this.resizable){
15778 this.resizer = new Roo.Resizable(this.list, {
15779 pinned:true, handles:'se'
15781 this.resizer.on('resize', function(r, w, h){
15782 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15783 this.listWidth = w;
15784 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15785 this.restrictHeight();
15787 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15790 if(!this.editable){
15791 this.editable = true;
15792 this.setEditable(false);
15797 if (typeof(this.events.add.listeners) != 'undefined') {
15799 this.addicon = this.wrap.createChild(
15800 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15802 this.addicon.on('click', function(e) {
15803 this.fireEvent('add', this);
15806 if (typeof(this.events.edit.listeners) != 'undefined') {
15808 this.editicon = this.wrap.createChild(
15809 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15810 if (this.addicon) {
15811 this.editicon.setStyle('margin-left', '40px');
15813 this.editicon.on('click', function(e) {
15815 // we fire even if inothing is selected..
15816 this.fireEvent('edit', this, this.lastData );
15822 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15823 "up" : function(e){
15824 this.inKeyMode = true;
15828 "down" : function(e){
15829 if(!this.isExpanded()){
15830 this.onTriggerClick();
15832 this.inKeyMode = true;
15837 "enter" : function(e){
15838 // this.onViewClick();
15842 if(this.fireEvent("specialkey", this, e)){
15843 this.onViewClick(false);
15849 "esc" : function(e){
15853 "tab" : function(e){
15856 if(this.fireEvent("specialkey", this, e)){
15857 this.onViewClick(false);
15865 doRelay : function(foo, bar, hname){
15866 if(hname == 'down' || this.scope.isExpanded()){
15867 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15876 this.queryDelay = Math.max(this.queryDelay || 10,
15877 this.mode == 'local' ? 10 : 250);
15880 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15882 if(this.typeAhead){
15883 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15885 if(this.editable !== false){
15886 this.inputEl().on("keyup", this.onKeyUp, this);
15888 if(this.forceSelection){
15889 this.inputEl().on('blur', this.doForce, this);
15893 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15894 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15898 initTickableEvents: function()
15902 if(this.hiddenName){
15904 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15906 this.hiddenField.dom.value =
15907 this.hiddenValue !== undefined ? this.hiddenValue :
15908 this.value !== undefined ? this.value : '';
15910 // prevent input submission
15911 this.el.dom.removeAttribute('name');
15912 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15917 // this.list = this.el.select('ul.dropdown-menu',true).first();
15919 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15920 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15921 if(this.triggerList){
15922 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15925 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15926 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15928 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15929 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15931 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15932 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15934 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15935 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15936 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15939 this.cancelBtn.hide();
15944 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15945 _this.list.setWidth(lw);
15948 this.list.on('mouseover', this.onViewOver, this);
15949 this.list.on('mousemove', this.onViewMove, this);
15951 this.list.on('scroll', this.onViewScroll, this);
15954 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15955 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15958 this.view = new Roo.View(this.list, this.tpl, {
15963 selectedClass: this.selectedClass
15966 //this.view.wrapEl.setDisplayed(false);
15967 this.view.on('click', this.onViewClick, this);
15971 this.store.on('beforeload', this.onBeforeLoad, this);
15972 this.store.on('load', this.onLoad, this);
15973 this.store.on('loadexception', this.onLoadException, this);
15976 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15977 "up" : function(e){
15978 this.inKeyMode = true;
15982 "down" : function(e){
15983 this.inKeyMode = true;
15987 "enter" : function(e){
15988 if(this.fireEvent("specialkey", this, e)){
15989 this.onViewClick(false);
15995 "esc" : function(e){
15996 this.onTickableFooterButtonClick(e, false, false);
15999 "tab" : function(e){
16000 this.fireEvent("specialkey", this, e);
16002 this.onTickableFooterButtonClick(e, false, false);
16009 doRelay : function(e, fn, key){
16010 if(this.scope.isExpanded()){
16011 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16020 this.queryDelay = Math.max(this.queryDelay || 10,
16021 this.mode == 'local' ? 10 : 250);
16024 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16026 if(this.typeAhead){
16027 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16030 if(this.editable !== false){
16031 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16034 this.indicator = this.indicatorEl();
16036 if(this.indicator){
16037 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16038 this.indicator.hide();
16043 onDestroy : function(){
16045 this.view.setStore(null);
16046 this.view.el.removeAllListeners();
16047 this.view.el.remove();
16048 this.view.purgeListeners();
16051 this.list.dom.innerHTML = '';
16055 this.store.un('beforeload', this.onBeforeLoad, this);
16056 this.store.un('load', this.onLoad, this);
16057 this.store.un('loadexception', this.onLoadException, this);
16059 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16063 fireKey : function(e){
16064 if(e.isNavKeyPress() && !this.list.isVisible()){
16065 this.fireEvent("specialkey", this, e);
16070 onResize: function(w, h)
16074 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16076 // if(typeof w != 'number'){
16077 // // we do not handle it!?!?
16080 // var tw = this.trigger.getWidth();
16081 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16082 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16084 // this.inputEl().setWidth( this.adjustWidth('input', x));
16086 // //this.trigger.setStyle('left', x+'px');
16088 // if(this.list && this.listWidth === undefined){
16089 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16090 // this.list.setWidth(lw);
16091 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16099 * Allow or prevent the user from directly editing the field text. If false is passed,
16100 * the user will only be able to select from the items defined in the dropdown list. This method
16101 * is the runtime equivalent of setting the 'editable' config option at config time.
16102 * @param {Boolean} value True to allow the user to directly edit the field text
16104 setEditable : function(value){
16105 if(value == this.editable){
16108 this.editable = value;
16110 this.inputEl().dom.setAttribute('readOnly', true);
16111 this.inputEl().on('mousedown', this.onTriggerClick, this);
16112 this.inputEl().addClass('x-combo-noedit');
16114 this.inputEl().dom.setAttribute('readOnly', false);
16115 this.inputEl().un('mousedown', this.onTriggerClick, this);
16116 this.inputEl().removeClass('x-combo-noedit');
16122 onBeforeLoad : function(combo,opts){
16123 if(!this.hasFocus){
16127 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16129 this.restrictHeight();
16130 this.selectedIndex = -1;
16134 onLoad : function(){
16136 this.hasQuery = false;
16138 if(!this.hasFocus){
16142 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16143 this.loading.hide();
16146 if(this.store.getCount() > 0){
16149 this.restrictHeight();
16150 if(this.lastQuery == this.allQuery){
16151 if(this.editable && !this.tickable){
16152 this.inputEl().dom.select();
16156 !this.selectByValue(this.value, true) &&
16159 !this.store.lastOptions ||
16160 typeof(this.store.lastOptions.add) == 'undefined' ||
16161 this.store.lastOptions.add != true
16164 this.select(0, true);
16167 if(this.autoFocus){
16170 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16171 this.taTask.delay(this.typeAheadDelay);
16175 this.onEmptyResults();
16181 onLoadException : function()
16183 this.hasQuery = false;
16185 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16186 this.loading.hide();
16189 if(this.tickable && this.editable){
16194 // only causes errors at present
16195 //Roo.log(this.store.reader.jsonData);
16196 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16198 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16204 onTypeAhead : function(){
16205 if(this.store.getCount() > 0){
16206 var r = this.store.getAt(0);
16207 var newValue = r.data[this.displayField];
16208 var len = newValue.length;
16209 var selStart = this.getRawValue().length;
16211 if(selStart != len){
16212 this.setRawValue(newValue);
16213 this.selectText(selStart, newValue.length);
16219 onSelect : function(record, index){
16221 if(this.fireEvent('beforeselect', this, record, index) !== false){
16223 this.setFromData(index > -1 ? record.data : false);
16226 this.fireEvent('select', this, record, index);
16231 * Returns the currently selected field value or empty string if no value is set.
16232 * @return {String} value The selected value
16234 getValue : function()
16236 if(Roo.isIOS && this.useNativeIOS){
16237 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16241 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16244 if(this.valueField){
16245 return typeof this.value != 'undefined' ? this.value : '';
16247 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16251 getRawValue : function()
16253 if(Roo.isIOS && this.useNativeIOS){
16254 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16257 var v = this.inputEl().getValue();
16263 * Clears any text/value currently set in the field
16265 clearValue : function(){
16267 if(this.hiddenField){
16268 this.hiddenField.dom.value = '';
16271 this.setRawValue('');
16272 this.lastSelectionText = '';
16273 this.lastData = false;
16275 var close = this.closeTriggerEl();
16286 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16287 * will be displayed in the field. If the value does not match the data value of an existing item,
16288 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16289 * Otherwise the field will be blank (although the value will still be set).
16290 * @param {String} value The value to match
16292 setValue : function(v)
16294 if(Roo.isIOS && this.useNativeIOS){
16295 this.setIOSValue(v);
16305 if(this.valueField){
16306 var r = this.findRecord(this.valueField, v);
16308 text = r.data[this.displayField];
16309 }else if(this.valueNotFoundText !== undefined){
16310 text = this.valueNotFoundText;
16313 this.lastSelectionText = text;
16314 if(this.hiddenField){
16315 this.hiddenField.dom.value = v;
16317 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16320 var close = this.closeTriggerEl();
16323 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16329 * @property {Object} the last set data for the element
16334 * Sets the value of the field based on a object which is related to the record format for the store.
16335 * @param {Object} value the value to set as. or false on reset?
16337 setFromData : function(o){
16344 var dv = ''; // display value
16345 var vv = ''; // value value..
16347 if (this.displayField) {
16348 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16350 // this is an error condition!!!
16351 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16354 if(this.valueField){
16355 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16358 var close = this.closeTriggerEl();
16361 if(dv.length || vv * 1 > 0){
16363 this.blockFocus=true;
16369 if(this.hiddenField){
16370 this.hiddenField.dom.value = vv;
16372 this.lastSelectionText = dv;
16373 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16377 // no hidden field.. - we store the value in 'value', but still display
16378 // display field!!!!
16379 this.lastSelectionText = dv;
16380 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16387 reset : function(){
16388 // overridden so that last data is reset..
16395 this.setValue(this.originalValue);
16396 //this.clearInvalid();
16397 this.lastData = false;
16399 this.view.clearSelections();
16405 findRecord : function(prop, value){
16407 if(this.store.getCount() > 0){
16408 this.store.each(function(r){
16409 if(r.data[prop] == value){
16419 getName: function()
16421 // returns hidden if it's set..
16422 if (!this.rendered) {return ''};
16423 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16427 onViewMove : function(e, t){
16428 this.inKeyMode = false;
16432 onViewOver : function(e, t){
16433 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16436 var item = this.view.findItemFromChild(t);
16439 var index = this.view.indexOf(item);
16440 this.select(index, false);
16445 onViewClick : function(view, doFocus, el, e)
16447 var index = this.view.getSelectedIndexes()[0];
16449 var r = this.store.getAt(index);
16453 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16460 Roo.each(this.tickItems, function(v,k){
16462 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16464 _this.tickItems.splice(k, 1);
16466 if(typeof(e) == 'undefined' && view == false){
16467 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16479 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16480 this.tickItems.push(r.data);
16483 if(typeof(e) == 'undefined' && view == false){
16484 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16491 this.onSelect(r, index);
16493 if(doFocus !== false && !this.blockFocus){
16494 this.inputEl().focus();
16499 restrictHeight : function(){
16500 //this.innerList.dom.style.height = '';
16501 //var inner = this.innerList.dom;
16502 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16503 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16504 //this.list.beginUpdate();
16505 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16506 this.list.alignTo(this.inputEl(), this.listAlign);
16507 this.list.alignTo(this.inputEl(), this.listAlign);
16508 //this.list.endUpdate();
16512 onEmptyResults : function(){
16514 if(this.tickable && this.editable){
16515 this.hasFocus = false;
16516 this.restrictHeight();
16524 * Returns true if the dropdown list is expanded, else false.
16526 isExpanded : function(){
16527 return this.list.isVisible();
16531 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16532 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16533 * @param {String} value The data value of the item to select
16534 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16535 * selected item if it is not currently in view (defaults to true)
16536 * @return {Boolean} True if the value matched an item in the list, else false
16538 selectByValue : function(v, scrollIntoView){
16539 if(v !== undefined && v !== null){
16540 var r = this.findRecord(this.valueField || this.displayField, v);
16542 this.select(this.store.indexOf(r), scrollIntoView);
16550 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16551 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16552 * @param {Number} index The zero-based index of the list item to select
16553 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16554 * selected item if it is not currently in view (defaults to true)
16556 select : function(index, scrollIntoView){
16557 this.selectedIndex = index;
16558 this.view.select(index);
16559 if(scrollIntoView !== false){
16560 var el = this.view.getNode(index);
16562 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16565 this.list.scrollChildIntoView(el, false);
16571 selectNext : function(){
16572 var ct = this.store.getCount();
16574 if(this.selectedIndex == -1){
16576 }else if(this.selectedIndex < ct-1){
16577 this.select(this.selectedIndex+1);
16583 selectPrev : function(){
16584 var ct = this.store.getCount();
16586 if(this.selectedIndex == -1){
16588 }else if(this.selectedIndex != 0){
16589 this.select(this.selectedIndex-1);
16595 onKeyUp : function(e){
16596 if(this.editable !== false && !e.isSpecialKey()){
16597 this.lastKey = e.getKey();
16598 this.dqTask.delay(this.queryDelay);
16603 validateBlur : function(){
16604 return !this.list || !this.list.isVisible();
16608 initQuery : function(){
16610 var v = this.getRawValue();
16612 if(this.tickable && this.editable){
16613 v = this.tickableInputEl().getValue();
16620 doForce : function(){
16621 if(this.inputEl().dom.value.length > 0){
16622 this.inputEl().dom.value =
16623 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16629 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16630 * query allowing the query action to be canceled if needed.
16631 * @param {String} query The SQL query to execute
16632 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16633 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16634 * saved in the current store (defaults to false)
16636 doQuery : function(q, forceAll){
16638 if(q === undefined || q === null){
16643 forceAll: forceAll,
16647 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16652 forceAll = qe.forceAll;
16653 if(forceAll === true || (q.length >= this.minChars)){
16655 this.hasQuery = true;
16657 if(this.lastQuery != q || this.alwaysQuery){
16658 this.lastQuery = q;
16659 if(this.mode == 'local'){
16660 this.selectedIndex = -1;
16662 this.store.clearFilter();
16665 if(this.specialFilter){
16666 this.fireEvent('specialfilter', this);
16671 this.store.filter(this.displayField, q);
16674 this.store.fireEvent("datachanged", this.store);
16681 this.store.baseParams[this.queryParam] = q;
16683 var options = {params : this.getParams(q)};
16686 options.add = true;
16687 options.params.start = this.page * this.pageSize;
16690 this.store.load(options);
16693 * this code will make the page width larger, at the beginning, the list not align correctly,
16694 * we should expand the list on onLoad
16695 * so command out it
16700 this.selectedIndex = -1;
16705 this.loadNext = false;
16709 getParams : function(q){
16711 //p[this.queryParam] = q;
16715 p.limit = this.pageSize;
16721 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16723 collapse : function(){
16724 if(!this.isExpanded()){
16730 this.hasFocus = false;
16734 this.cancelBtn.hide();
16735 this.trigger.show();
16738 this.tickableInputEl().dom.value = '';
16739 this.tickableInputEl().blur();
16744 Roo.get(document).un('mousedown', this.collapseIf, this);
16745 Roo.get(document).un('mousewheel', this.collapseIf, this);
16746 if (!this.editable) {
16747 Roo.get(document).un('keydown', this.listKeyPress, this);
16749 this.fireEvent('collapse', this);
16755 collapseIf : function(e){
16756 var in_combo = e.within(this.el);
16757 var in_list = e.within(this.list);
16758 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16760 if (in_combo || in_list || is_list) {
16761 //e.stopPropagation();
16766 this.onTickableFooterButtonClick(e, false, false);
16774 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16776 expand : function(){
16778 if(this.isExpanded() || !this.hasFocus){
16782 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16783 this.list.setWidth(lw);
16789 this.restrictHeight();
16793 this.tickItems = Roo.apply([], this.item);
16796 this.cancelBtn.show();
16797 this.trigger.hide();
16800 this.tickableInputEl().focus();
16805 Roo.get(document).on('mousedown', this.collapseIf, this);
16806 Roo.get(document).on('mousewheel', this.collapseIf, this);
16807 if (!this.editable) {
16808 Roo.get(document).on('keydown', this.listKeyPress, this);
16811 this.fireEvent('expand', this);
16815 // Implements the default empty TriggerField.onTriggerClick function
16816 onTriggerClick : function(e)
16818 Roo.log('trigger click');
16820 if(this.disabled || !this.triggerList){
16825 this.loadNext = false;
16827 if(this.isExpanded()){
16829 if (!this.blockFocus) {
16830 this.inputEl().focus();
16834 this.hasFocus = true;
16835 if(this.triggerAction == 'all') {
16836 this.doQuery(this.allQuery, true);
16838 this.doQuery(this.getRawValue());
16840 if (!this.blockFocus) {
16841 this.inputEl().focus();
16846 onTickableTriggerClick : function(e)
16853 this.loadNext = false;
16854 this.hasFocus = true;
16856 if(this.triggerAction == 'all') {
16857 this.doQuery(this.allQuery, true);
16859 this.doQuery(this.getRawValue());
16863 onSearchFieldClick : function(e)
16865 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16866 this.onTickableFooterButtonClick(e, false, false);
16870 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16875 this.loadNext = false;
16876 this.hasFocus = true;
16878 if(this.triggerAction == 'all') {
16879 this.doQuery(this.allQuery, true);
16881 this.doQuery(this.getRawValue());
16885 listKeyPress : function(e)
16887 //Roo.log('listkeypress');
16888 // scroll to first matching element based on key pres..
16889 if (e.isSpecialKey()) {
16892 var k = String.fromCharCode(e.getKey()).toUpperCase();
16895 var csel = this.view.getSelectedNodes();
16896 var cselitem = false;
16898 var ix = this.view.indexOf(csel[0]);
16899 cselitem = this.store.getAt(ix);
16900 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16906 this.store.each(function(v) {
16908 // start at existing selection.
16909 if (cselitem.id == v.id) {
16915 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16916 match = this.store.indexOf(v);
16922 if (match === false) {
16923 return true; // no more action?
16926 this.view.select(match);
16927 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16928 sn.scrollIntoView(sn.dom.parentNode, false);
16931 onViewScroll : function(e, t){
16933 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){
16937 this.hasQuery = true;
16939 this.loading = this.list.select('.loading', true).first();
16941 if(this.loading === null){
16942 this.list.createChild({
16944 cls: 'loading roo-select2-more-results roo-select2-active',
16945 html: 'Loading more results...'
16948 this.loading = this.list.select('.loading', true).first();
16950 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16952 this.loading.hide();
16955 this.loading.show();
16960 this.loadNext = true;
16962 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16967 addItem : function(o)
16969 var dv = ''; // display value
16971 if (this.displayField) {
16972 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16974 // this is an error condition!!!
16975 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16982 var choice = this.choices.createChild({
16984 cls: 'roo-select2-search-choice',
16993 cls: 'roo-select2-search-choice-close fa fa-times',
16998 }, this.searchField);
17000 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17002 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17010 this.inputEl().dom.value = '';
17015 onRemoveItem : function(e, _self, o)
17017 e.preventDefault();
17019 this.lastItem = Roo.apply([], this.item);
17021 var index = this.item.indexOf(o.data) * 1;
17024 Roo.log('not this item?!');
17028 this.item.splice(index, 1);
17033 this.fireEvent('remove', this, e);
17039 syncValue : function()
17041 if(!this.item.length){
17048 Roo.each(this.item, function(i){
17049 if(_this.valueField){
17050 value.push(i[_this.valueField]);
17057 this.value = value.join(',');
17059 if(this.hiddenField){
17060 this.hiddenField.dom.value = this.value;
17063 this.store.fireEvent("datachanged", this.store);
17068 clearItem : function()
17070 if(!this.multiple){
17076 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17084 if(this.tickable && !Roo.isTouch){
17085 this.view.refresh();
17089 inputEl: function ()
17091 if(Roo.isIOS && this.useNativeIOS){
17092 return this.el.select('select.roo-ios-select', true).first();
17095 if(Roo.isTouch && this.mobileTouchView){
17096 return this.el.select('input.form-control',true).first();
17100 return this.searchField;
17103 return this.el.select('input.form-control',true).first();
17106 onTickableFooterButtonClick : function(e, btn, el)
17108 e.preventDefault();
17110 this.lastItem = Roo.apply([], this.item);
17112 if(btn && btn.name == 'cancel'){
17113 this.tickItems = Roo.apply([], this.item);
17122 Roo.each(this.tickItems, function(o){
17130 validate : function()
17132 if(this.getVisibilityEl().hasClass('hidden')){
17136 var v = this.getRawValue();
17139 v = this.getValue();
17142 if(this.disabled || this.allowBlank || v.length){
17147 this.markInvalid();
17151 tickableInputEl : function()
17153 if(!this.tickable || !this.editable){
17154 return this.inputEl();
17157 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17161 getAutoCreateTouchView : function()
17166 cls: 'form-group' //input-group
17172 type : this.inputType,
17173 cls : 'form-control x-combo-noedit',
17174 autocomplete: 'new-password',
17175 placeholder : this.placeholder || '',
17180 input.name = this.name;
17184 input.cls += ' input-' + this.size;
17187 if (this.disabled) {
17188 input.disabled = true;
17192 cls : 'roo-combobox-wrap',
17199 inputblock.cls += ' input-group';
17201 inputblock.cn.unshift({
17203 cls : 'input-group-addon input-group-prepend input-group-text',
17208 if(this.removable && !this.multiple){
17209 inputblock.cls += ' roo-removable';
17211 inputblock.cn.push({
17214 cls : 'roo-combo-removable-btn close'
17218 if(this.hasFeedback && !this.allowBlank){
17220 inputblock.cls += ' has-feedback';
17222 inputblock.cn.push({
17224 cls: 'glyphicon form-control-feedback'
17231 inputblock.cls += (this.before) ? '' : ' input-group';
17233 inputblock.cn.push({
17235 cls : 'input-group-addon input-group-append input-group-text',
17241 var ibwrap = inputblock;
17246 cls: 'roo-select2-choices',
17250 cls: 'roo-select2-search-field',
17263 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17268 cls: 'form-hidden-field'
17274 if(!this.multiple && this.showToggleBtn){
17280 if (this.caret != false) {
17283 cls: 'fa fa-' + this.caret
17290 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17292 Roo.bootstrap.version == 3 ? caret : '',
17295 cls: 'combobox-clear',
17309 combobox.cls += ' roo-select2-container-multi';
17312 var align = this.labelAlign || this.parentLabelAlign();
17314 if (align ==='left' && this.fieldLabel.length) {
17319 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17320 tooltip : 'This field is required'
17324 cls : 'control-label col-form-label',
17325 html : this.fieldLabel
17329 cls : 'roo-combobox-wrap ',
17336 var labelCfg = cfg.cn[1];
17337 var contentCfg = cfg.cn[2];
17340 if(this.indicatorpos == 'right'){
17345 cls : 'control-label col-form-label',
17349 html : this.fieldLabel
17353 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17354 tooltip : 'This field is required'
17359 cls : "roo-combobox-wrap ",
17367 labelCfg = cfg.cn[0];
17368 contentCfg = cfg.cn[1];
17373 if(this.labelWidth > 12){
17374 labelCfg.style = "width: " + this.labelWidth + 'px';
17377 if(this.labelWidth < 13 && this.labelmd == 0){
17378 this.labelmd = this.labelWidth;
17381 if(this.labellg > 0){
17382 labelCfg.cls += ' col-lg-' + this.labellg;
17383 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17386 if(this.labelmd > 0){
17387 labelCfg.cls += ' col-md-' + this.labelmd;
17388 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17391 if(this.labelsm > 0){
17392 labelCfg.cls += ' col-sm-' + this.labelsm;
17393 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17396 if(this.labelxs > 0){
17397 labelCfg.cls += ' col-xs-' + this.labelxs;
17398 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17402 } else if ( this.fieldLabel.length) {
17406 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17407 tooltip : 'This field is required'
17411 cls : 'control-label',
17412 html : this.fieldLabel
17423 if(this.indicatorpos == 'right'){
17427 cls : 'control-label',
17428 html : this.fieldLabel,
17432 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17433 tooltip : 'This field is required'
17450 var settings = this;
17452 ['xs','sm','md','lg'].map(function(size){
17453 if (settings[size]) {
17454 cfg.cls += ' col-' + size + '-' + settings[size];
17461 initTouchView : function()
17463 this.renderTouchView();
17465 this.touchViewEl.on('scroll', function(){
17466 this.el.dom.scrollTop = 0;
17469 this.originalValue = this.getValue();
17471 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17473 this.inputEl().on("click", this.showTouchView, this);
17474 if (this.triggerEl) {
17475 this.triggerEl.on("click", this.showTouchView, this);
17479 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17480 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17482 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17484 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17485 this.store.on('load', this.onTouchViewLoad, this);
17486 this.store.on('loadexception', this.onTouchViewLoadException, this);
17488 if(this.hiddenName){
17490 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17492 this.hiddenField.dom.value =
17493 this.hiddenValue !== undefined ? this.hiddenValue :
17494 this.value !== undefined ? this.value : '';
17496 this.el.dom.removeAttribute('name');
17497 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17501 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17502 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17505 if(this.removable && !this.multiple){
17506 var close = this.closeTriggerEl();
17508 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17509 close.on('click', this.removeBtnClick, this, close);
17513 * fix the bug in Safari iOS8
17515 this.inputEl().on("focus", function(e){
17516 document.activeElement.blur();
17519 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17526 renderTouchView : function()
17528 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17529 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17531 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17532 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17534 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17535 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17536 this.touchViewBodyEl.setStyle('overflow', 'auto');
17538 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17539 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17541 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17542 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17546 showTouchView : function()
17552 this.touchViewHeaderEl.hide();
17554 if(this.modalTitle.length){
17555 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17556 this.touchViewHeaderEl.show();
17559 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17560 this.touchViewEl.show();
17562 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17564 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17565 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17567 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17569 if(this.modalTitle.length){
17570 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17573 this.touchViewBodyEl.setHeight(bodyHeight);
17577 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17579 this.touchViewEl.addClass(['in','show']);
17582 if(this._touchViewMask){
17583 Roo.get(document.body).addClass("x-body-masked");
17584 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17585 this._touchViewMask.setStyle('z-index', 10000);
17586 this._touchViewMask.addClass('show');
17589 this.doTouchViewQuery();
17593 hideTouchView : function()
17595 this.touchViewEl.removeClass(['in','show']);
17599 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17601 this.touchViewEl.setStyle('display', 'none');
17604 if(this._touchViewMask){
17605 this._touchViewMask.removeClass('show');
17606 Roo.get(document.body).removeClass("x-body-masked");
17610 setTouchViewValue : function()
17617 Roo.each(this.tickItems, function(o){
17622 this.hideTouchView();
17625 doTouchViewQuery : function()
17634 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17638 if(!this.alwaysQuery || this.mode == 'local'){
17639 this.onTouchViewLoad();
17646 onTouchViewBeforeLoad : function(combo,opts)
17652 onTouchViewLoad : function()
17654 if(this.store.getCount() < 1){
17655 this.onTouchViewEmptyResults();
17659 this.clearTouchView();
17661 var rawValue = this.getRawValue();
17663 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17665 this.tickItems = [];
17667 this.store.data.each(function(d, rowIndex){
17668 var row = this.touchViewListGroup.createChild(template);
17670 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17671 row.addClass(d.data.cls);
17674 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17677 html : d.data[this.displayField]
17680 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17681 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17684 row.removeClass('selected');
17685 if(!this.multiple && this.valueField &&
17686 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17689 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17690 row.addClass('selected');
17693 if(this.multiple && this.valueField &&
17694 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17698 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17699 this.tickItems.push(d.data);
17702 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17706 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17708 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17710 if(this.modalTitle.length){
17711 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17714 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17716 if(this.mobile_restrict_height && listHeight < bodyHeight){
17717 this.touchViewBodyEl.setHeight(listHeight);
17722 if(firstChecked && listHeight > bodyHeight){
17723 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17728 onTouchViewLoadException : function()
17730 this.hideTouchView();
17733 onTouchViewEmptyResults : function()
17735 this.clearTouchView();
17737 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17739 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17743 clearTouchView : function()
17745 this.touchViewListGroup.dom.innerHTML = '';
17748 onTouchViewClick : function(e, el, o)
17750 e.preventDefault();
17753 var rowIndex = o.rowIndex;
17755 var r = this.store.getAt(rowIndex);
17757 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17759 if(!this.multiple){
17760 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17761 c.dom.removeAttribute('checked');
17764 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17766 this.setFromData(r.data);
17768 var close = this.closeTriggerEl();
17774 this.hideTouchView();
17776 this.fireEvent('select', this, r, rowIndex);
17781 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17782 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17783 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17787 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17788 this.addItem(r.data);
17789 this.tickItems.push(r.data);
17793 getAutoCreateNativeIOS : function()
17796 cls: 'form-group' //input-group,
17801 cls : 'roo-ios-select'
17805 combobox.name = this.name;
17808 if (this.disabled) {
17809 combobox.disabled = true;
17812 var settings = this;
17814 ['xs','sm','md','lg'].map(function(size){
17815 if (settings[size]) {
17816 cfg.cls += ' col-' + size + '-' + settings[size];
17826 initIOSView : function()
17828 this.store.on('load', this.onIOSViewLoad, this);
17833 onIOSViewLoad : function()
17835 if(this.store.getCount() < 1){
17839 this.clearIOSView();
17841 if(this.allowBlank) {
17843 var default_text = '-- SELECT --';
17845 if(this.placeholder.length){
17846 default_text = this.placeholder;
17849 if(this.emptyTitle.length){
17850 default_text += ' - ' + this.emptyTitle + ' -';
17853 var opt = this.inputEl().createChild({
17856 html : default_text
17860 o[this.valueField] = 0;
17861 o[this.displayField] = default_text;
17863 this.ios_options.push({
17870 this.store.data.each(function(d, rowIndex){
17874 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17875 html = d.data[this.displayField];
17880 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17881 value = d.data[this.valueField];
17890 if(this.value == d.data[this.valueField]){
17891 option['selected'] = true;
17894 var opt = this.inputEl().createChild(option);
17896 this.ios_options.push({
17903 this.inputEl().on('change', function(){
17904 this.fireEvent('select', this);
17909 clearIOSView: function()
17911 this.inputEl().dom.innerHTML = '';
17913 this.ios_options = [];
17916 setIOSValue: function(v)
17920 if(!this.ios_options){
17924 Roo.each(this.ios_options, function(opts){
17926 opts.el.dom.removeAttribute('selected');
17928 if(opts.data[this.valueField] != v){
17932 opts.el.dom.setAttribute('selected', true);
17938 * @cfg {Boolean} grow
17942 * @cfg {Number} growMin
17946 * @cfg {Number} growMax
17955 Roo.apply(Roo.bootstrap.ComboBox, {
17959 cls: 'modal-header',
17981 cls: 'list-group-item',
17985 cls: 'roo-combobox-list-group-item-value'
17989 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18003 listItemCheckbox : {
18005 cls: 'list-group-item',
18009 cls: 'roo-combobox-list-group-item-value'
18013 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18029 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18034 cls: 'modal-footer',
18042 cls: 'col-xs-6 text-left',
18045 cls: 'btn btn-danger roo-touch-view-cancel',
18051 cls: 'col-xs-6 text-right',
18054 cls: 'btn btn-success roo-touch-view-ok',
18065 Roo.apply(Roo.bootstrap.ComboBox, {
18067 touchViewTemplate : {
18069 cls: 'modal fade roo-combobox-touch-view',
18073 cls: 'modal-dialog',
18074 style : 'position:fixed', // we have to fix position....
18078 cls: 'modal-content',
18080 Roo.bootstrap.ComboBox.header,
18081 Roo.bootstrap.ComboBox.body,
18082 Roo.bootstrap.ComboBox.footer
18091 * Ext JS Library 1.1.1
18092 * Copyright(c) 2006-2007, Ext JS, LLC.
18094 * Originally Released Under LGPL - original licence link has changed is not relivant.
18097 * <script type="text/javascript">
18102 * @extends Roo.util.Observable
18103 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18104 * This class also supports single and multi selection modes. <br>
18105 * Create a data model bound view:
18107 var store = new Roo.data.Store(...);
18109 var view = new Roo.View({
18111 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18113 singleSelect: true,
18114 selectedClass: "ydataview-selected",
18118 // listen for node click?
18119 view.on("click", function(vw, index, node, e){
18120 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18124 dataModel.load("foobar.xml");
18126 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18128 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18129 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18131 * Note: old style constructor is still suported (container, template, config)
18134 * Create a new View
18135 * @param {Object} config The config object
18138 Roo.View = function(config, depreciated_tpl, depreciated_config){
18140 this.parent = false;
18142 if (typeof(depreciated_tpl) == 'undefined') {
18143 // new way.. - universal constructor.
18144 Roo.apply(this, config);
18145 this.el = Roo.get(this.el);
18148 this.el = Roo.get(config);
18149 this.tpl = depreciated_tpl;
18150 Roo.apply(this, depreciated_config);
18152 this.wrapEl = this.el.wrap().wrap();
18153 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18156 if(typeof(this.tpl) == "string"){
18157 this.tpl = new Roo.Template(this.tpl);
18159 // support xtype ctors..
18160 this.tpl = new Roo.factory(this.tpl, Roo);
18164 this.tpl.compile();
18169 * @event beforeclick
18170 * Fires before a click is processed. Returns false to cancel the default action.
18171 * @param {Roo.View} this
18172 * @param {Number} index The index of the target node
18173 * @param {HTMLElement} node The target node
18174 * @param {Roo.EventObject} e The raw event object
18176 "beforeclick" : true,
18179 * Fires when a template node is clicked.
18180 * @param {Roo.View} this
18181 * @param {Number} index The index of the target node
18182 * @param {HTMLElement} node The target node
18183 * @param {Roo.EventObject} e The raw event object
18188 * Fires when a template node is double clicked.
18189 * @param {Roo.View} this
18190 * @param {Number} index The index of the target node
18191 * @param {HTMLElement} node The target node
18192 * @param {Roo.EventObject} e The raw event object
18196 * @event contextmenu
18197 * Fires when a template node is right clicked.
18198 * @param {Roo.View} this
18199 * @param {Number} index The index of the target node
18200 * @param {HTMLElement} node The target node
18201 * @param {Roo.EventObject} e The raw event object
18203 "contextmenu" : true,
18205 * @event selectionchange
18206 * Fires when the selected nodes change.
18207 * @param {Roo.View} this
18208 * @param {Array} selections Array of the selected nodes
18210 "selectionchange" : true,
18213 * @event beforeselect
18214 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18215 * @param {Roo.View} this
18216 * @param {HTMLElement} node The node to be selected
18217 * @param {Array} selections Array of currently selected nodes
18219 "beforeselect" : true,
18221 * @event preparedata
18222 * Fires on every row to render, to allow you to change the data.
18223 * @param {Roo.View} this
18224 * @param {Object} data to be rendered (change this)
18226 "preparedata" : true
18234 "click": this.onClick,
18235 "dblclick": this.onDblClick,
18236 "contextmenu": this.onContextMenu,
18240 this.selections = [];
18242 this.cmp = new Roo.CompositeElementLite([]);
18244 this.store = Roo.factory(this.store, Roo.data);
18245 this.setStore(this.store, true);
18248 if ( this.footer && this.footer.xtype) {
18250 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18252 this.footer.dataSource = this.store;
18253 this.footer.container = fctr;
18254 this.footer = Roo.factory(this.footer, Roo);
18255 fctr.insertFirst(this.el);
18257 // this is a bit insane - as the paging toolbar seems to detach the el..
18258 // dom.parentNode.parentNode.parentNode
18259 // they get detached?
18263 Roo.View.superclass.constructor.call(this);
18268 Roo.extend(Roo.View, Roo.util.Observable, {
18271 * @cfg {Roo.data.Store} store Data store to load data from.
18276 * @cfg {String|Roo.Element} el The container element.
18281 * @cfg {String|Roo.Template} tpl The template used by this View
18285 * @cfg {String} dataName the named area of the template to use as the data area
18286 * Works with domtemplates roo-name="name"
18290 * @cfg {String} selectedClass The css class to add to selected nodes
18292 selectedClass : "x-view-selected",
18294 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18299 * @cfg {String} text to display on mask (default Loading)
18303 * @cfg {Boolean} multiSelect Allow multiple selection
18305 multiSelect : false,
18307 * @cfg {Boolean} singleSelect Allow single selection
18309 singleSelect: false,
18312 * @cfg {Boolean} toggleSelect - selecting
18314 toggleSelect : false,
18317 * @cfg {Boolean} tickable - selecting
18322 * Returns the element this view is bound to.
18323 * @return {Roo.Element}
18325 getEl : function(){
18326 return this.wrapEl;
18332 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18334 refresh : function(){
18335 //Roo.log('refresh');
18338 // if we are using something like 'domtemplate', then
18339 // the what gets used is:
18340 // t.applySubtemplate(NAME, data, wrapping data..)
18341 // the outer template then get' applied with
18342 // the store 'extra data'
18343 // and the body get's added to the
18344 // roo-name="data" node?
18345 // <span class='roo-tpl-{name}'></span> ?????
18349 this.clearSelections();
18350 this.el.update("");
18352 var records = this.store.getRange();
18353 if(records.length < 1) {
18355 // is this valid?? = should it render a template??
18357 this.el.update(this.emptyText);
18361 if (this.dataName) {
18362 this.el.update(t.apply(this.store.meta)); //????
18363 el = this.el.child('.roo-tpl-' + this.dataName);
18366 for(var i = 0, len = records.length; i < len; i++){
18367 var data = this.prepareData(records[i].data, i, records[i]);
18368 this.fireEvent("preparedata", this, data, i, records[i]);
18370 var d = Roo.apply({}, data);
18373 Roo.apply(d, {'roo-id' : Roo.id()});
18377 Roo.each(this.parent.item, function(item){
18378 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18381 Roo.apply(d, {'roo-data-checked' : 'checked'});
18385 html[html.length] = Roo.util.Format.trim(
18387 t.applySubtemplate(this.dataName, d, this.store.meta) :
18394 el.update(html.join(""));
18395 this.nodes = el.dom.childNodes;
18396 this.updateIndexes(0);
18401 * Function to override to reformat the data that is sent to
18402 * the template for each node.
18403 * DEPRICATED - use the preparedata event handler.
18404 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18405 * a JSON object for an UpdateManager bound view).
18407 prepareData : function(data, index, record)
18409 this.fireEvent("preparedata", this, data, index, record);
18413 onUpdate : function(ds, record){
18414 // Roo.log('on update');
18415 this.clearSelections();
18416 var index = this.store.indexOf(record);
18417 var n = this.nodes[index];
18418 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18419 n.parentNode.removeChild(n);
18420 this.updateIndexes(index, index);
18426 onAdd : function(ds, records, index)
18428 //Roo.log(['on Add', ds, records, index] );
18429 this.clearSelections();
18430 if(this.nodes.length == 0){
18434 var n = this.nodes[index];
18435 for(var i = 0, len = records.length; i < len; i++){
18436 var d = this.prepareData(records[i].data, i, records[i]);
18438 this.tpl.insertBefore(n, d);
18441 this.tpl.append(this.el, d);
18444 this.updateIndexes(index);
18447 onRemove : function(ds, record, index){
18448 // Roo.log('onRemove');
18449 this.clearSelections();
18450 var el = this.dataName ?
18451 this.el.child('.roo-tpl-' + this.dataName) :
18454 el.dom.removeChild(this.nodes[index]);
18455 this.updateIndexes(index);
18459 * Refresh an individual node.
18460 * @param {Number} index
18462 refreshNode : function(index){
18463 this.onUpdate(this.store, this.store.getAt(index));
18466 updateIndexes : function(startIndex, endIndex){
18467 var ns = this.nodes;
18468 startIndex = startIndex || 0;
18469 endIndex = endIndex || ns.length - 1;
18470 for(var i = startIndex; i <= endIndex; i++){
18471 ns[i].nodeIndex = i;
18476 * Changes the data store this view uses and refresh the view.
18477 * @param {Store} store
18479 setStore : function(store, initial){
18480 if(!initial && this.store){
18481 this.store.un("datachanged", this.refresh);
18482 this.store.un("add", this.onAdd);
18483 this.store.un("remove", this.onRemove);
18484 this.store.un("update", this.onUpdate);
18485 this.store.un("clear", this.refresh);
18486 this.store.un("beforeload", this.onBeforeLoad);
18487 this.store.un("load", this.onLoad);
18488 this.store.un("loadexception", this.onLoad);
18492 store.on("datachanged", this.refresh, this);
18493 store.on("add", this.onAdd, this);
18494 store.on("remove", this.onRemove, this);
18495 store.on("update", this.onUpdate, this);
18496 store.on("clear", this.refresh, this);
18497 store.on("beforeload", this.onBeforeLoad, this);
18498 store.on("load", this.onLoad, this);
18499 store.on("loadexception", this.onLoad, this);
18507 * onbeforeLoad - masks the loading area.
18510 onBeforeLoad : function(store,opts)
18512 //Roo.log('onBeforeLoad');
18514 this.el.update("");
18516 this.el.mask(this.mask ? this.mask : "Loading" );
18518 onLoad : function ()
18525 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18526 * @param {HTMLElement} node
18527 * @return {HTMLElement} The template node
18529 findItemFromChild : function(node){
18530 var el = this.dataName ?
18531 this.el.child('.roo-tpl-' + this.dataName,true) :
18534 if(!node || node.parentNode == el){
18537 var p = node.parentNode;
18538 while(p && p != el){
18539 if(p.parentNode == el){
18548 onClick : function(e){
18549 var item = this.findItemFromChild(e.getTarget());
18551 var index = this.indexOf(item);
18552 if(this.onItemClick(item, index, e) !== false){
18553 this.fireEvent("click", this, index, item, e);
18556 this.clearSelections();
18561 onContextMenu : function(e){
18562 var item = this.findItemFromChild(e.getTarget());
18564 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18569 onDblClick : function(e){
18570 var item = this.findItemFromChild(e.getTarget());
18572 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18576 onItemClick : function(item, index, e)
18578 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18581 if (this.toggleSelect) {
18582 var m = this.isSelected(item) ? 'unselect' : 'select';
18585 _t[m](item, true, false);
18588 if(this.multiSelect || this.singleSelect){
18589 if(this.multiSelect && e.shiftKey && this.lastSelection){
18590 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18592 this.select(item, this.multiSelect && e.ctrlKey);
18593 this.lastSelection = item;
18596 if(!this.tickable){
18597 e.preventDefault();
18605 * Get the number of selected nodes.
18608 getSelectionCount : function(){
18609 return this.selections.length;
18613 * Get the currently selected nodes.
18614 * @return {Array} An array of HTMLElements
18616 getSelectedNodes : function(){
18617 return this.selections;
18621 * Get the indexes of the selected nodes.
18624 getSelectedIndexes : function(){
18625 var indexes = [], s = this.selections;
18626 for(var i = 0, len = s.length; i < len; i++){
18627 indexes.push(s[i].nodeIndex);
18633 * Clear all selections
18634 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18636 clearSelections : function(suppressEvent){
18637 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18638 this.cmp.elements = this.selections;
18639 this.cmp.removeClass(this.selectedClass);
18640 this.selections = [];
18641 if(!suppressEvent){
18642 this.fireEvent("selectionchange", this, this.selections);
18648 * Returns true if the passed node is selected
18649 * @param {HTMLElement/Number} node The node or node index
18650 * @return {Boolean}
18652 isSelected : function(node){
18653 var s = this.selections;
18657 node = this.getNode(node);
18658 return s.indexOf(node) !== -1;
18663 * @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
18664 * @param {Boolean} keepExisting (optional) true to keep existing selections
18665 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18667 select : function(nodeInfo, keepExisting, suppressEvent){
18668 if(nodeInfo instanceof Array){
18670 this.clearSelections(true);
18672 for(var i = 0, len = nodeInfo.length; i < len; i++){
18673 this.select(nodeInfo[i], true, true);
18677 var node = this.getNode(nodeInfo);
18678 if(!node || this.isSelected(node)){
18679 return; // already selected.
18682 this.clearSelections(true);
18685 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18686 Roo.fly(node).addClass(this.selectedClass);
18687 this.selections.push(node);
18688 if(!suppressEvent){
18689 this.fireEvent("selectionchange", this, this.selections);
18697 * @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
18698 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18699 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18701 unselect : function(nodeInfo, keepExisting, suppressEvent)
18703 if(nodeInfo instanceof Array){
18704 Roo.each(this.selections, function(s) {
18705 this.unselect(s, nodeInfo);
18709 var node = this.getNode(nodeInfo);
18710 if(!node || !this.isSelected(node)){
18711 //Roo.log("not selected");
18712 return; // not selected.
18716 Roo.each(this.selections, function(s) {
18718 Roo.fly(node).removeClass(this.selectedClass);
18725 this.selections= ns;
18726 this.fireEvent("selectionchange", this, this.selections);
18730 * Gets a template node.
18731 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18732 * @return {HTMLElement} The node or null if it wasn't found
18734 getNode : function(nodeInfo){
18735 if(typeof nodeInfo == "string"){
18736 return document.getElementById(nodeInfo);
18737 }else if(typeof nodeInfo == "number"){
18738 return this.nodes[nodeInfo];
18744 * Gets a range template nodes.
18745 * @param {Number} startIndex
18746 * @param {Number} endIndex
18747 * @return {Array} An array of nodes
18749 getNodes : function(start, end){
18750 var ns = this.nodes;
18751 start = start || 0;
18752 end = typeof end == "undefined" ? ns.length - 1 : end;
18755 for(var i = start; i <= end; i++){
18759 for(var i = start; i >= end; i--){
18767 * Finds the index of the passed node
18768 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18769 * @return {Number} The index of the node or -1
18771 indexOf : function(node){
18772 node = this.getNode(node);
18773 if(typeof node.nodeIndex == "number"){
18774 return node.nodeIndex;
18776 var ns = this.nodes;
18777 for(var i = 0, len = ns.length; i < len; i++){
18788 * based on jquery fullcalendar
18792 Roo.bootstrap = Roo.bootstrap || {};
18794 * @class Roo.bootstrap.Calendar
18795 * @extends Roo.bootstrap.Component
18796 * Bootstrap Calendar class
18797 * @cfg {Boolean} loadMask (true|false) default false
18798 * @cfg {Object} header generate the user specific header of the calendar, default false
18801 * Create a new Container
18802 * @param {Object} config The config object
18807 Roo.bootstrap.Calendar = function(config){
18808 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18812 * Fires when a date is selected
18813 * @param {DatePicker} this
18814 * @param {Date} date The selected date
18818 * @event monthchange
18819 * Fires when the displayed month changes
18820 * @param {DatePicker} this
18821 * @param {Date} date The selected month
18823 'monthchange': true,
18825 * @event evententer
18826 * Fires when mouse over an event
18827 * @param {Calendar} this
18828 * @param {event} Event
18830 'evententer': true,
18832 * @event eventleave
18833 * Fires when the mouse leaves an
18834 * @param {Calendar} this
18837 'eventleave': true,
18839 * @event eventclick
18840 * Fires when the mouse click an
18841 * @param {Calendar} this
18850 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18853 * @cfg {Number} startDay
18854 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18862 getAutoCreate : function(){
18865 var fc_button = function(name, corner, style, content ) {
18866 return Roo.apply({},{
18868 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18870 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18873 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18884 style : 'width:100%',
18891 cls : 'fc-header-left',
18893 fc_button('prev', 'left', 'arrow', '‹' ),
18894 fc_button('next', 'right', 'arrow', '›' ),
18895 { tag: 'span', cls: 'fc-header-space' },
18896 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18904 cls : 'fc-header-center',
18908 cls: 'fc-header-title',
18911 html : 'month / year'
18919 cls : 'fc-header-right',
18921 /* fc_button('month', 'left', '', 'month' ),
18922 fc_button('week', '', '', 'week' ),
18923 fc_button('day', 'right', '', 'day' )
18935 header = this.header;
18938 var cal_heads = function() {
18940 // fixme - handle this.
18942 for (var i =0; i < Date.dayNames.length; i++) {
18943 var d = Date.dayNames[i];
18946 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18947 html : d.substring(0,3)
18951 ret[0].cls += ' fc-first';
18952 ret[6].cls += ' fc-last';
18955 var cal_cell = function(n) {
18958 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18963 cls: 'fc-day-number',
18967 cls: 'fc-day-content',
18971 style: 'position: relative;' // height: 17px;
18983 var cal_rows = function() {
18986 for (var r = 0; r < 6; r++) {
18993 for (var i =0; i < Date.dayNames.length; i++) {
18994 var d = Date.dayNames[i];
18995 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18998 row.cn[0].cls+=' fc-first';
18999 row.cn[0].cn[0].style = 'min-height:90px';
19000 row.cn[6].cls+=' fc-last';
19004 ret[0].cls += ' fc-first';
19005 ret[4].cls += ' fc-prev-last';
19006 ret[5].cls += ' fc-last';
19013 cls: 'fc-border-separate',
19014 style : 'width:100%',
19022 cls : 'fc-first fc-last',
19040 cls : 'fc-content',
19041 style : "position: relative;",
19044 cls : 'fc-view fc-view-month fc-grid',
19045 style : 'position: relative',
19046 unselectable : 'on',
19049 cls : 'fc-event-container',
19050 style : 'position:absolute;z-index:8;top:0;left:0;'
19068 initEvents : function()
19071 throw "can not find store for calendar";
19077 style: "text-align:center",
19081 style: "background-color:white;width:50%;margin:250 auto",
19085 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19096 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19098 var size = this.el.select('.fc-content', true).first().getSize();
19099 this.maskEl.setSize(size.width, size.height);
19100 this.maskEl.enableDisplayMode("block");
19101 if(!this.loadMask){
19102 this.maskEl.hide();
19105 this.store = Roo.factory(this.store, Roo.data);
19106 this.store.on('load', this.onLoad, this);
19107 this.store.on('beforeload', this.onBeforeLoad, this);
19111 this.cells = this.el.select('.fc-day',true);
19112 //Roo.log(this.cells);
19113 this.textNodes = this.el.query('.fc-day-number');
19114 this.cells.addClassOnOver('fc-state-hover');
19116 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19117 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19118 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19119 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19121 this.on('monthchange', this.onMonthChange, this);
19123 this.update(new Date().clearTime());
19126 resize : function() {
19127 var sz = this.el.getSize();
19129 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19130 this.el.select('.fc-day-content div',true).setHeight(34);
19135 showPrevMonth : function(e){
19136 this.update(this.activeDate.add("mo", -1));
19138 showToday : function(e){
19139 this.update(new Date().clearTime());
19142 showNextMonth : function(e){
19143 this.update(this.activeDate.add("mo", 1));
19147 showPrevYear : function(){
19148 this.update(this.activeDate.add("y", -1));
19152 showNextYear : function(){
19153 this.update(this.activeDate.add("y", 1));
19158 update : function(date)
19160 var vd = this.activeDate;
19161 this.activeDate = date;
19162 // if(vd && this.el){
19163 // var t = date.getTime();
19164 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19165 // Roo.log('using add remove');
19167 // this.fireEvent('monthchange', this, date);
19169 // this.cells.removeClass("fc-state-highlight");
19170 // this.cells.each(function(c){
19171 // if(c.dateValue == t){
19172 // c.addClass("fc-state-highlight");
19173 // setTimeout(function(){
19174 // try{c.dom.firstChild.focus();}catch(e){}
19184 var days = date.getDaysInMonth();
19186 var firstOfMonth = date.getFirstDateOfMonth();
19187 var startingPos = firstOfMonth.getDay()-this.startDay;
19189 if(startingPos < this.startDay){
19193 var pm = date.add(Date.MONTH, -1);
19194 var prevStart = pm.getDaysInMonth()-startingPos;
19196 this.cells = this.el.select('.fc-day',true);
19197 this.textNodes = this.el.query('.fc-day-number');
19198 this.cells.addClassOnOver('fc-state-hover');
19200 var cells = this.cells.elements;
19201 var textEls = this.textNodes;
19203 Roo.each(cells, function(cell){
19204 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19207 days += startingPos;
19209 // convert everything to numbers so it's fast
19210 var day = 86400000;
19211 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19214 //Roo.log(prevStart);
19216 var today = new Date().clearTime().getTime();
19217 var sel = date.clearTime().getTime();
19218 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19219 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19220 var ddMatch = this.disabledDatesRE;
19221 var ddText = this.disabledDatesText;
19222 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19223 var ddaysText = this.disabledDaysText;
19224 var format = this.format;
19226 var setCellClass = function(cal, cell){
19230 //Roo.log('set Cell Class');
19232 var t = d.getTime();
19236 cell.dateValue = t;
19238 cell.className += " fc-today";
19239 cell.className += " fc-state-highlight";
19240 cell.title = cal.todayText;
19243 // disable highlight in other month..
19244 //cell.className += " fc-state-highlight";
19249 cell.className = " fc-state-disabled";
19250 cell.title = cal.minText;
19254 cell.className = " fc-state-disabled";
19255 cell.title = cal.maxText;
19259 if(ddays.indexOf(d.getDay()) != -1){
19260 cell.title = ddaysText;
19261 cell.className = " fc-state-disabled";
19264 if(ddMatch && format){
19265 var fvalue = d.dateFormat(format);
19266 if(ddMatch.test(fvalue)){
19267 cell.title = ddText.replace("%0", fvalue);
19268 cell.className = " fc-state-disabled";
19272 if (!cell.initialClassName) {
19273 cell.initialClassName = cell.dom.className;
19276 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19281 for(; i < startingPos; i++) {
19282 textEls[i].innerHTML = (++prevStart);
19283 d.setDate(d.getDate()+1);
19285 cells[i].className = "fc-past fc-other-month";
19286 setCellClass(this, cells[i]);
19291 for(; i < days; i++){
19292 intDay = i - startingPos + 1;
19293 textEls[i].innerHTML = (intDay);
19294 d.setDate(d.getDate()+1);
19296 cells[i].className = ''; // "x-date-active";
19297 setCellClass(this, cells[i]);
19301 for(; i < 42; i++) {
19302 textEls[i].innerHTML = (++extraDays);
19303 d.setDate(d.getDate()+1);
19305 cells[i].className = "fc-future fc-other-month";
19306 setCellClass(this, cells[i]);
19309 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19311 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19313 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19314 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19316 if(totalRows != 6){
19317 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19318 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19321 this.fireEvent('monthchange', this, date);
19325 if(!this.internalRender){
19326 var main = this.el.dom.firstChild;
19327 var w = main.offsetWidth;
19328 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19329 Roo.fly(main).setWidth(w);
19330 this.internalRender = true;
19331 // opera does not respect the auto grow header center column
19332 // then, after it gets a width opera refuses to recalculate
19333 // without a second pass
19334 if(Roo.isOpera && !this.secondPass){
19335 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19336 this.secondPass = true;
19337 this.update.defer(10, this, [date]);
19344 findCell : function(dt) {
19345 dt = dt.clearTime().getTime();
19347 this.cells.each(function(c){
19348 //Roo.log("check " +c.dateValue + '?=' + dt);
19349 if(c.dateValue == dt){
19359 findCells : function(ev) {
19360 var s = ev.start.clone().clearTime().getTime();
19362 var e= ev.end.clone().clearTime().getTime();
19365 this.cells.each(function(c){
19366 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19368 if(c.dateValue > e){
19371 if(c.dateValue < s){
19380 // findBestRow: function(cells)
19384 // for (var i =0 ; i < cells.length;i++) {
19385 // ret = Math.max(cells[i].rows || 0,ret);
19392 addItem : function(ev)
19394 // look for vertical location slot in
19395 var cells = this.findCells(ev);
19397 // ev.row = this.findBestRow(cells);
19399 // work out the location.
19403 for(var i =0; i < cells.length; i++) {
19405 cells[i].row = cells[0].row;
19408 cells[i].row = cells[i].row + 1;
19418 if (crow.start.getY() == cells[i].getY()) {
19420 crow.end = cells[i];
19437 cells[0].events.push(ev);
19439 this.calevents.push(ev);
19442 clearEvents: function() {
19444 if(!this.calevents){
19448 Roo.each(this.cells.elements, function(c){
19454 Roo.each(this.calevents, function(e) {
19455 Roo.each(e.els, function(el) {
19456 el.un('mouseenter' ,this.onEventEnter, this);
19457 el.un('mouseleave' ,this.onEventLeave, this);
19462 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19468 renderEvents: function()
19472 this.cells.each(function(c) {
19481 if(c.row != c.events.length){
19482 r = 4 - (4 - (c.row - c.events.length));
19485 c.events = ev.slice(0, r);
19486 c.more = ev.slice(r);
19488 if(c.more.length && c.more.length == 1){
19489 c.events.push(c.more.pop());
19492 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19496 this.cells.each(function(c) {
19498 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19501 for (var e = 0; e < c.events.length; e++){
19502 var ev = c.events[e];
19503 var rows = ev.rows;
19505 for(var i = 0; i < rows.length; i++) {
19507 // how many rows should it span..
19510 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19511 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19513 unselectable : "on",
19516 cls: 'fc-event-inner',
19520 // cls: 'fc-event-time',
19521 // html : cells.length > 1 ? '' : ev.time
19525 cls: 'fc-event-title',
19526 html : String.format('{0}', ev.title)
19533 cls: 'ui-resizable-handle ui-resizable-e',
19534 html : '  '
19541 cfg.cls += ' fc-event-start';
19543 if ((i+1) == rows.length) {
19544 cfg.cls += ' fc-event-end';
19547 var ctr = _this.el.select('.fc-event-container',true).first();
19548 var cg = ctr.createChild(cfg);
19550 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19551 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19553 var r = (c.more.length) ? 1 : 0;
19554 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19555 cg.setWidth(ebox.right - sbox.x -2);
19557 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19558 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19559 cg.on('click', _this.onEventClick, _this, ev);
19570 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19571 style : 'position: absolute',
19572 unselectable : "on",
19575 cls: 'fc-event-inner',
19579 cls: 'fc-event-title',
19587 cls: 'ui-resizable-handle ui-resizable-e',
19588 html : '  '
19594 var ctr = _this.el.select('.fc-event-container',true).first();
19595 var cg = ctr.createChild(cfg);
19597 var sbox = c.select('.fc-day-content',true).first().getBox();
19598 var ebox = c.select('.fc-day-content',true).first().getBox();
19600 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19601 cg.setWidth(ebox.right - sbox.x -2);
19603 cg.on('click', _this.onMoreEventClick, _this, c.more);
19613 onEventEnter: function (e, el,event,d) {
19614 this.fireEvent('evententer', this, el, event);
19617 onEventLeave: function (e, el,event,d) {
19618 this.fireEvent('eventleave', this, el, event);
19621 onEventClick: function (e, el,event,d) {
19622 this.fireEvent('eventclick', this, el, event);
19625 onMonthChange: function () {
19629 onMoreEventClick: function(e, el, more)
19633 this.calpopover.placement = 'right';
19634 this.calpopover.setTitle('More');
19636 this.calpopover.setContent('');
19638 var ctr = this.calpopover.el.select('.popover-content', true).first();
19640 Roo.each(more, function(m){
19642 cls : 'fc-event-hori fc-event-draggable',
19645 var cg = ctr.createChild(cfg);
19647 cg.on('click', _this.onEventClick, _this, m);
19650 this.calpopover.show(el);
19655 onLoad: function ()
19657 this.calevents = [];
19660 if(this.store.getCount() > 0){
19661 this.store.data.each(function(d){
19664 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19665 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19666 time : d.data.start_time,
19667 title : d.data.title,
19668 description : d.data.description,
19669 venue : d.data.venue
19674 this.renderEvents();
19676 if(this.calevents.length && this.loadMask){
19677 this.maskEl.hide();
19681 onBeforeLoad: function()
19683 this.clearEvents();
19685 this.maskEl.show();
19699 * @class Roo.bootstrap.Popover
19700 * @extends Roo.bootstrap.Component
19701 * Bootstrap Popover class
19702 * @cfg {String} html contents of the popover (or false to use children..)
19703 * @cfg {String} title of popover (or false to hide)
19704 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19705 * @cfg {String} trigger click || hover (or false to trigger manually)
19706 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19707 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19708 * - if false and it has a 'parent' then it will be automatically added to that element
19709 * - if string - Roo.get will be called
19710 * @cfg {Number} delay - delay before showing
19713 * Create a new Popover
19714 * @param {Object} config The config object
19717 Roo.bootstrap.Popover = function(config){
19718 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19724 * After the popover show
19726 * @param {Roo.bootstrap.Popover} this
19731 * After the popover hide
19733 * @param {Roo.bootstrap.Popover} this
19739 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19744 placement : 'right',
19745 trigger : 'hover', // hover
19751 can_build_overlaid : false,
19753 maskEl : false, // the mask element
19756 alignEl : false, // when show is called with an element - this get's stored.
19758 getChildContainer : function()
19760 return this.contentEl;
19763 getPopoverHeader : function()
19765 this.title = true; // flag not to hide it..
19766 this.headerEl.addClass('p-0');
19767 return this.headerEl
19771 getAutoCreate : function(){
19774 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19775 style: 'display:block',
19781 cls : 'popover-inner ',
19785 cls: 'popover-title popover-header',
19786 html : this.title === false ? '' : this.title
19789 cls : 'popover-content popover-body ' + (this.cls || ''),
19790 html : this.html || ''
19801 * @param {string} the title
19803 setTitle: function(str)
19807 this.headerEl.dom.innerHTML = str;
19812 * @param {string} the body content
19814 setContent: function(str)
19817 if (this.contentEl) {
19818 this.contentEl.dom.innerHTML = str;
19822 // as it get's added to the bottom of the page.
19823 onRender : function(ct, position)
19825 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19830 var cfg = Roo.apply({}, this.getAutoCreate());
19834 cfg.cls += ' ' + this.cls;
19837 cfg.style = this.style;
19839 //Roo.log("adding to ");
19840 this.el = Roo.get(document.body).createChild(cfg, position);
19841 // Roo.log(this.el);
19844 this.contentEl = this.el.select('.popover-content',true).first();
19845 this.headerEl = this.el.select('.popover-title',true).first();
19848 if(typeof(this.items) != 'undefined'){
19849 var items = this.items;
19852 for(var i =0;i < items.length;i++) {
19853 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19857 this.items = nitems;
19859 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19860 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19867 resizeMask : function()
19869 this.maskEl.setSize(
19870 Roo.lib.Dom.getViewWidth(true),
19871 Roo.lib.Dom.getViewHeight(true)
19875 initEvents : function()
19879 Roo.bootstrap.Popover.register(this);
19882 this.arrowEl = this.el.select('.arrow',true).first();
19883 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19884 this.el.enableDisplayMode('block');
19888 if (this.over === false && !this.parent()) {
19891 if (this.triggers === false) {
19896 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19897 var triggers = this.trigger ? this.trigger.split(' ') : [];
19898 Roo.each(triggers, function(trigger) {
19900 if (trigger == 'click') {
19901 on_el.on('click', this.toggle, this);
19902 } else if (trigger != 'manual') {
19903 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19904 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19906 on_el.on(eventIn ,this.enter, this);
19907 on_el.on(eventOut, this.leave, this);
19917 toggle : function () {
19918 this.hoverState == 'in' ? this.leave() : this.enter();
19921 enter : function () {
19923 clearTimeout(this.timeout);
19925 this.hoverState = 'in';
19927 if (!this.delay || !this.delay.show) {
19932 this.timeout = setTimeout(function () {
19933 if (_t.hoverState == 'in') {
19936 }, this.delay.show)
19939 leave : function() {
19940 clearTimeout(this.timeout);
19942 this.hoverState = 'out';
19944 if (!this.delay || !this.delay.hide) {
19949 this.timeout = setTimeout(function () {
19950 if (_t.hoverState == 'out') {
19953 }, this.delay.hide)
19957 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19958 * @param {string} (left|right|top|bottom) position
19960 show : function (on_el, placement)
19962 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19963 on_el = on_el || false; // default to false
19966 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19967 on_el = this.parent().el;
19968 } else if (this.over) {
19969 Roo.get(this.over);
19974 this.alignEl = Roo.get( on_el );
19977 this.render(document.body);
19983 if (this.title === false) {
19984 this.headerEl.hide();
19989 this.el.dom.style.display = 'block';
19992 if (this.alignEl) {
19993 this.updatePosition(this.placement, true);
19996 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19997 var es = this.el.getSize();
19998 var x = Roo.lib.Dom.getViewWidth()/2;
19999 var y = Roo.lib.Dom.getViewHeight()/2;
20000 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20005 //var arrow = this.el.select('.arrow',true).first();
20006 //arrow.set(align[2],
20008 this.el.addClass('in');
20012 this.hoverState = 'in';
20015 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20016 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20017 this.maskEl.dom.style.display = 'block';
20018 this.maskEl.addClass('show');
20020 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20022 this.fireEvent('show', this);
20026 * fire this manually after loading a grid in the table for example
20027 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20028 * @param {Boolean} try and move it if we cant get right position.
20030 updatePosition : function(placement, try_move)
20032 // allow for calling with no parameters
20033 placement = placement ? placement : this.placement;
20034 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20036 this.el.removeClass([
20037 'fade','top','bottom', 'left', 'right','in',
20038 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20040 this.el.addClass(placement + ' bs-popover-' + placement);
20042 if (!this.alignEl ) {
20046 switch (placement) {
20048 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20049 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20050 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20051 //normal display... or moved up/down.
20052 this.el.setXY(offset);
20053 var xy = this.alignEl.getAnchorXY('tr', false);
20055 this.arrowEl.setXY(xy);
20058 // continue through...
20059 return this.updatePosition('left', false);
20063 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20064 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20065 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20066 //normal display... or moved up/down.
20067 this.el.setXY(offset);
20068 var xy = this.alignEl.getAnchorXY('tl', false);
20069 xy[0]-=10;xy[1]+=5; // << fix me
20070 this.arrowEl.setXY(xy);
20074 return this.updatePosition('right', false);
20077 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20078 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20079 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20080 //normal display... or moved up/down.
20081 this.el.setXY(offset);
20082 var xy = this.alignEl.getAnchorXY('t', false);
20083 xy[1]-=10; // << fix me
20084 this.arrowEl.setXY(xy);
20088 return this.updatePosition('bottom', false);
20091 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20092 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20093 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20094 //normal display... or moved up/down.
20095 this.el.setXY(offset);
20096 var xy = this.alignEl.getAnchorXY('b', false);
20097 xy[1]+=2; // << fix me
20098 this.arrowEl.setXY(xy);
20102 return this.updatePosition('top', false);
20113 this.el.setXY([0,0]);
20114 this.el.removeClass('in');
20116 this.hoverState = null;
20117 this.maskEl.hide(); // always..
20118 this.fireEvent('hide', this);
20124 Roo.apply(Roo.bootstrap.Popover, {
20127 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20128 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20129 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20130 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20135 clickHander : false,
20138 onMouseDown : function(e)
20140 if (!e.getTarget(".roo-popover")) {
20148 register : function(popup)
20150 if (!Roo.bootstrap.Popover.clickHandler) {
20151 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20153 // hide other popups.
20155 this.popups.push(popup);
20157 hideAll : function()
20159 this.popups.forEach(function(p) {
20167 * Card header - holder for the card header elements.
20172 * @class Roo.bootstrap.PopoverNav
20173 * @extends Roo.bootstrap.NavGroup
20174 * Bootstrap Popover header navigation class
20176 * Create a new Popover Header Navigation
20177 * @param {Object} config The config object
20180 Roo.bootstrap.PopoverNav = function(config){
20181 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20184 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20187 container_method : 'getPopoverHeader'
20205 * @class Roo.bootstrap.Progress
20206 * @extends Roo.bootstrap.Component
20207 * Bootstrap Progress class
20208 * @cfg {Boolean} striped striped of the progress bar
20209 * @cfg {Boolean} active animated of the progress bar
20213 * Create a new Progress
20214 * @param {Object} config The config object
20217 Roo.bootstrap.Progress = function(config){
20218 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20221 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20226 getAutoCreate : function(){
20234 cfg.cls += ' progress-striped';
20238 cfg.cls += ' active';
20257 * @class Roo.bootstrap.ProgressBar
20258 * @extends Roo.bootstrap.Component
20259 * Bootstrap ProgressBar class
20260 * @cfg {Number} aria_valuenow aria-value now
20261 * @cfg {Number} aria_valuemin aria-value min
20262 * @cfg {Number} aria_valuemax aria-value max
20263 * @cfg {String} label label for the progress bar
20264 * @cfg {String} panel (success | info | warning | danger )
20265 * @cfg {String} role role of the progress bar
20266 * @cfg {String} sr_only text
20270 * Create a new ProgressBar
20271 * @param {Object} config The config object
20274 Roo.bootstrap.ProgressBar = function(config){
20275 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20278 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20282 aria_valuemax : 100,
20288 getAutoCreate : function()
20293 cls: 'progress-bar',
20294 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20306 cfg.role = this.role;
20309 if(this.aria_valuenow){
20310 cfg['aria-valuenow'] = this.aria_valuenow;
20313 if(this.aria_valuemin){
20314 cfg['aria-valuemin'] = this.aria_valuemin;
20317 if(this.aria_valuemax){
20318 cfg['aria-valuemax'] = this.aria_valuemax;
20321 if(this.label && !this.sr_only){
20322 cfg.html = this.label;
20326 cfg.cls += ' progress-bar-' + this.panel;
20332 update : function(aria_valuenow)
20334 this.aria_valuenow = aria_valuenow;
20336 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20351 * @class Roo.bootstrap.TabGroup
20352 * @extends Roo.bootstrap.Column
20353 * Bootstrap Column class
20354 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20355 * @cfg {Boolean} carousel true to make the group behave like a carousel
20356 * @cfg {Boolean} bullets show bullets for the panels
20357 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20358 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20359 * @cfg {Boolean} showarrow (true|false) show arrow default true
20362 * Create a new TabGroup
20363 * @param {Object} config The config object
20366 Roo.bootstrap.TabGroup = function(config){
20367 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20369 this.navId = Roo.id();
20372 Roo.bootstrap.TabGroup.register(this);
20376 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20379 transition : false,
20384 slideOnTouch : false,
20387 getAutoCreate : function()
20389 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20391 cfg.cls += ' tab-content';
20393 if (this.carousel) {
20394 cfg.cls += ' carousel slide';
20397 cls : 'carousel-inner',
20401 if(this.bullets && !Roo.isTouch){
20404 cls : 'carousel-bullets',
20408 if(this.bullets_cls){
20409 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20416 cfg.cn[0].cn.push(bullets);
20419 if(this.showarrow){
20420 cfg.cn[0].cn.push({
20422 class : 'carousel-arrow',
20426 class : 'carousel-prev',
20430 class : 'fa fa-chevron-left'
20436 class : 'carousel-next',
20440 class : 'fa fa-chevron-right'
20453 initEvents: function()
20455 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20456 // this.el.on("touchstart", this.onTouchStart, this);
20459 if(this.autoslide){
20462 this.slideFn = window.setInterval(function() {
20463 _this.showPanelNext();
20467 if(this.showarrow){
20468 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20469 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20475 // onTouchStart : function(e, el, o)
20477 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20481 // this.showPanelNext();
20485 getChildContainer : function()
20487 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20491 * register a Navigation item
20492 * @param {Roo.bootstrap.NavItem} the navitem to add
20494 register : function(item)
20496 this.tabs.push( item);
20497 item.navId = this.navId; // not really needed..
20502 getActivePanel : function()
20505 Roo.each(this.tabs, function(t) {
20515 getPanelByName : function(n)
20518 Roo.each(this.tabs, function(t) {
20519 if (t.tabId == n) {
20527 indexOfPanel : function(p)
20530 Roo.each(this.tabs, function(t,i) {
20531 if (t.tabId == p.tabId) {
20540 * show a specific panel
20541 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20542 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20544 showPanel : function (pan)
20546 if(this.transition || typeof(pan) == 'undefined'){
20547 Roo.log("waiting for the transitionend");
20551 if (typeof(pan) == 'number') {
20552 pan = this.tabs[pan];
20555 if (typeof(pan) == 'string') {
20556 pan = this.getPanelByName(pan);
20559 var cur = this.getActivePanel();
20562 Roo.log('pan or acitve pan is undefined');
20566 if (pan.tabId == this.getActivePanel().tabId) {
20570 if (false === cur.fireEvent('beforedeactivate')) {
20574 if(this.bullets > 0 && !Roo.isTouch){
20575 this.setActiveBullet(this.indexOfPanel(pan));
20578 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20580 //class="carousel-item carousel-item-next carousel-item-left"
20582 this.transition = true;
20583 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20584 var lr = dir == 'next' ? 'left' : 'right';
20585 pan.el.addClass(dir); // or prev
20586 pan.el.addClass('carousel-item-' + dir); // or prev
20587 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20588 cur.el.addClass(lr); // or right
20589 pan.el.addClass(lr);
20590 cur.el.addClass('carousel-item-' +lr); // or right
20591 pan.el.addClass('carousel-item-' +lr);
20595 cur.el.on('transitionend', function() {
20596 Roo.log("trans end?");
20598 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20599 pan.setActive(true);
20601 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20602 cur.setActive(false);
20604 _this.transition = false;
20606 }, this, { single: true } );
20611 cur.setActive(false);
20612 pan.setActive(true);
20617 showPanelNext : function()
20619 var i = this.indexOfPanel(this.getActivePanel());
20621 if (i >= this.tabs.length - 1 && !this.autoslide) {
20625 if (i >= this.tabs.length - 1 && this.autoslide) {
20629 this.showPanel(this.tabs[i+1]);
20632 showPanelPrev : function()
20634 var i = this.indexOfPanel(this.getActivePanel());
20636 if (i < 1 && !this.autoslide) {
20640 if (i < 1 && this.autoslide) {
20641 i = this.tabs.length;
20644 this.showPanel(this.tabs[i-1]);
20648 addBullet: function()
20650 if(!this.bullets || Roo.isTouch){
20653 var ctr = this.el.select('.carousel-bullets',true).first();
20654 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20655 var bullet = ctr.createChild({
20656 cls : 'bullet bullet-' + i
20657 },ctr.dom.lastChild);
20662 bullet.on('click', (function(e, el, o, ii, t){
20664 e.preventDefault();
20666 this.showPanel(ii);
20668 if(this.autoslide && this.slideFn){
20669 clearInterval(this.slideFn);
20670 this.slideFn = window.setInterval(function() {
20671 _this.showPanelNext();
20675 }).createDelegate(this, [i, bullet], true));
20680 setActiveBullet : function(i)
20686 Roo.each(this.el.select('.bullet', true).elements, function(el){
20687 el.removeClass('selected');
20690 var bullet = this.el.select('.bullet-' + i, true).first();
20696 bullet.addClass('selected');
20707 Roo.apply(Roo.bootstrap.TabGroup, {
20711 * register a Navigation Group
20712 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20714 register : function(navgrp)
20716 this.groups[navgrp.navId] = navgrp;
20720 * fetch a Navigation Group based on the navigation ID
20721 * if one does not exist , it will get created.
20722 * @param {string} the navgroup to add
20723 * @returns {Roo.bootstrap.NavGroup} the navgroup
20725 get: function(navId) {
20726 if (typeof(this.groups[navId]) == 'undefined') {
20727 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20729 return this.groups[navId] ;
20744 * @class Roo.bootstrap.TabPanel
20745 * @extends Roo.bootstrap.Component
20746 * Bootstrap TabPanel class
20747 * @cfg {Boolean} active panel active
20748 * @cfg {String} html panel content
20749 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20750 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20751 * @cfg {String} href click to link..
20752 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20756 * Create a new TabPanel
20757 * @param {Object} config The config object
20760 Roo.bootstrap.TabPanel = function(config){
20761 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20765 * Fires when the active status changes
20766 * @param {Roo.bootstrap.TabPanel} this
20767 * @param {Boolean} state the new state
20772 * @event beforedeactivate
20773 * Fires before a tab is de-activated - can be used to do validation on a form.
20774 * @param {Roo.bootstrap.TabPanel} this
20775 * @return {Boolean} false if there is an error
20778 'beforedeactivate': true
20781 this.tabId = this.tabId || Roo.id();
20785 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20792 touchSlide : false,
20793 getAutoCreate : function(){
20798 // item is needed for carousel - not sure if it has any effect otherwise
20799 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20800 html: this.html || ''
20804 cfg.cls += ' active';
20808 cfg.tabId = this.tabId;
20816 initEvents: function()
20818 var p = this.parent();
20820 this.navId = this.navId || p.navId;
20822 if (typeof(this.navId) != 'undefined') {
20823 // not really needed.. but just in case.. parent should be a NavGroup.
20824 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20828 var i = tg.tabs.length - 1;
20830 if(this.active && tg.bullets > 0 && i < tg.bullets){
20831 tg.setActiveBullet(i);
20835 this.el.on('click', this.onClick, this);
20837 if(Roo.isTouch && this.touchSlide){
20838 this.el.on("touchstart", this.onTouchStart, this);
20839 this.el.on("touchmove", this.onTouchMove, this);
20840 this.el.on("touchend", this.onTouchEnd, this);
20845 onRender : function(ct, position)
20847 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20850 setActive : function(state)
20852 Roo.log("panel - set active " + this.tabId + "=" + state);
20854 this.active = state;
20856 this.el.removeClass('active');
20858 } else if (!this.el.hasClass('active')) {
20859 this.el.addClass('active');
20862 this.fireEvent('changed', this, state);
20865 onClick : function(e)
20867 e.preventDefault();
20869 if(!this.href.length){
20873 window.location.href = this.href;
20882 onTouchStart : function(e)
20884 this.swiping = false;
20886 this.startX = e.browserEvent.touches[0].clientX;
20887 this.startY = e.browserEvent.touches[0].clientY;
20890 onTouchMove : function(e)
20892 this.swiping = true;
20894 this.endX = e.browserEvent.touches[0].clientX;
20895 this.endY = e.browserEvent.touches[0].clientY;
20898 onTouchEnd : function(e)
20905 var tabGroup = this.parent();
20907 if(this.endX > this.startX){ // swiping right
20908 tabGroup.showPanelPrev();
20912 if(this.startX > this.endX){ // swiping left
20913 tabGroup.showPanelNext();
20932 * @class Roo.bootstrap.DateField
20933 * @extends Roo.bootstrap.Input
20934 * Bootstrap DateField class
20935 * @cfg {Number} weekStart default 0
20936 * @cfg {String} viewMode default empty, (months|years)
20937 * @cfg {String} minViewMode default empty, (months|years)
20938 * @cfg {Number} startDate default -Infinity
20939 * @cfg {Number} endDate default Infinity
20940 * @cfg {Boolean} todayHighlight default false
20941 * @cfg {Boolean} todayBtn default false
20942 * @cfg {Boolean} calendarWeeks default false
20943 * @cfg {Object} daysOfWeekDisabled default empty
20944 * @cfg {Boolean} singleMode default false (true | false)
20946 * @cfg {Boolean} keyboardNavigation default true
20947 * @cfg {String} language default en
20950 * Create a new DateField
20951 * @param {Object} config The config object
20954 Roo.bootstrap.DateField = function(config){
20955 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20959 * Fires when this field show.
20960 * @param {Roo.bootstrap.DateField} this
20961 * @param {Mixed} date The date value
20966 * Fires when this field hide.
20967 * @param {Roo.bootstrap.DateField} this
20968 * @param {Mixed} date The date value
20973 * Fires when select a date.
20974 * @param {Roo.bootstrap.DateField} this
20975 * @param {Mixed} date The date value
20979 * @event beforeselect
20980 * Fires when before select a date.
20981 * @param {Roo.bootstrap.DateField} this
20982 * @param {Mixed} date The date value
20984 beforeselect : true
20988 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20991 * @cfg {String} format
20992 * The default date format string which can be overriden for localization support. The format must be
20993 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20997 * @cfg {String} altFormats
20998 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20999 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21001 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21009 todayHighlight : false,
21015 keyboardNavigation: true,
21017 calendarWeeks: false,
21019 startDate: -Infinity,
21023 daysOfWeekDisabled: [],
21027 singleMode : false,
21029 UTCDate: function()
21031 return new Date(Date.UTC.apply(Date, arguments));
21034 UTCToday: function()
21036 var today = new Date();
21037 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21040 getDate: function() {
21041 var d = this.getUTCDate();
21042 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21045 getUTCDate: function() {
21049 setDate: function(d) {
21050 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21053 setUTCDate: function(d) {
21055 this.setValue(this.formatDate(this.date));
21058 onRender: function(ct, position)
21061 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21063 this.language = this.language || 'en';
21064 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21065 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21067 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21068 this.format = this.format || 'm/d/y';
21069 this.isInline = false;
21070 this.isInput = true;
21071 this.component = this.el.select('.add-on', true).first() || false;
21072 this.component = (this.component && this.component.length === 0) ? false : this.component;
21073 this.hasInput = this.component && this.inputEl().length;
21075 if (typeof(this.minViewMode === 'string')) {
21076 switch (this.minViewMode) {
21078 this.minViewMode = 1;
21081 this.minViewMode = 2;
21084 this.minViewMode = 0;
21089 if (typeof(this.viewMode === 'string')) {
21090 switch (this.viewMode) {
21103 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21105 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21107 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21109 this.picker().on('mousedown', this.onMousedown, this);
21110 this.picker().on('click', this.onClick, this);
21112 this.picker().addClass('datepicker-dropdown');
21114 this.startViewMode = this.viewMode;
21116 if(this.singleMode){
21117 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21118 v.setVisibilityMode(Roo.Element.DISPLAY);
21122 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21123 v.setStyle('width', '189px');
21127 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21128 if(!this.calendarWeeks){
21133 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21134 v.attr('colspan', function(i, val){
21135 return parseInt(val) + 1;
21140 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21142 this.setStartDate(this.startDate);
21143 this.setEndDate(this.endDate);
21145 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21152 if(this.isInline) {
21157 picker : function()
21159 return this.pickerEl;
21160 // return this.el.select('.datepicker', true).first();
21163 fillDow: function()
21165 var dowCnt = this.weekStart;
21174 if(this.calendarWeeks){
21182 while (dowCnt < this.weekStart + 7) {
21186 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21190 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21193 fillMonths: function()
21196 var months = this.picker().select('>.datepicker-months td', true).first();
21198 months.dom.innerHTML = '';
21204 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21207 months.createChild(month);
21214 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;
21216 if (this.date < this.startDate) {
21217 this.viewDate = new Date(this.startDate);
21218 } else if (this.date > this.endDate) {
21219 this.viewDate = new Date(this.endDate);
21221 this.viewDate = new Date(this.date);
21229 var d = new Date(this.viewDate),
21230 year = d.getUTCFullYear(),
21231 month = d.getUTCMonth(),
21232 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21233 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21234 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21235 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21236 currentDate = this.date && this.date.valueOf(),
21237 today = this.UTCToday();
21239 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21241 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21243 // this.picker.select('>tfoot th.today').
21244 // .text(dates[this.language].today)
21245 // .toggle(this.todayBtn !== false);
21247 this.updateNavArrows();
21250 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21252 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21254 prevMonth.setUTCDate(day);
21256 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21258 var nextMonth = new Date(prevMonth);
21260 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21262 nextMonth = nextMonth.valueOf();
21264 var fillMonths = false;
21266 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21268 while(prevMonth.valueOf() <= nextMonth) {
21271 if (prevMonth.getUTCDay() === this.weekStart) {
21273 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21281 if(this.calendarWeeks){
21282 // ISO 8601: First week contains first thursday.
21283 // ISO also states week starts on Monday, but we can be more abstract here.
21285 // Start of current week: based on weekstart/current date
21286 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21287 // Thursday of this week
21288 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21289 // First Thursday of year, year from thursday
21290 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21291 // Calendar week: ms between thursdays, div ms per day, div 7 days
21292 calWeek = (th - yth) / 864e5 / 7 + 1;
21294 fillMonths.cn.push({
21302 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21304 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21307 if (this.todayHighlight &&
21308 prevMonth.getUTCFullYear() == today.getFullYear() &&
21309 prevMonth.getUTCMonth() == today.getMonth() &&
21310 prevMonth.getUTCDate() == today.getDate()) {
21311 clsName += ' today';
21314 if (currentDate && prevMonth.valueOf() === currentDate) {
21315 clsName += ' active';
21318 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21319 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21320 clsName += ' disabled';
21323 fillMonths.cn.push({
21325 cls: 'day ' + clsName,
21326 html: prevMonth.getDate()
21329 prevMonth.setDate(prevMonth.getDate()+1);
21332 var currentYear = this.date && this.date.getUTCFullYear();
21333 var currentMonth = this.date && this.date.getUTCMonth();
21335 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21337 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21338 v.removeClass('active');
21340 if(currentYear === year && k === currentMonth){
21341 v.addClass('active');
21344 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21345 v.addClass('disabled');
21351 year = parseInt(year/10, 10) * 10;
21353 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21355 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21358 for (var i = -1; i < 11; i++) {
21359 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21361 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21369 showMode: function(dir)
21372 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21375 Roo.each(this.picker().select('>div',true).elements, function(v){
21376 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21379 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21384 if(this.isInline) {
21388 this.picker().removeClass(['bottom', 'top']);
21390 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21392 * place to the top of element!
21396 this.picker().addClass('top');
21397 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21402 this.picker().addClass('bottom');
21404 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21407 parseDate : function(value)
21409 if(!value || value instanceof Date){
21412 var v = Date.parseDate(value, this.format);
21413 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21414 v = Date.parseDate(value, 'Y-m-d');
21416 if(!v && this.altFormats){
21417 if(!this.altFormatsArray){
21418 this.altFormatsArray = this.altFormats.split("|");
21420 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21421 v = Date.parseDate(value, this.altFormatsArray[i]);
21427 formatDate : function(date, fmt)
21429 return (!date || !(date instanceof Date)) ?
21430 date : date.dateFormat(fmt || this.format);
21433 onFocus : function()
21435 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21439 onBlur : function()
21441 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21443 var d = this.inputEl().getValue();
21450 showPopup : function()
21452 this.picker().show();
21456 this.fireEvent('showpopup', this, this.date);
21459 hidePopup : function()
21461 if(this.isInline) {
21464 this.picker().hide();
21465 this.viewMode = this.startViewMode;
21468 this.fireEvent('hidepopup', this, this.date);
21472 onMousedown: function(e)
21474 e.stopPropagation();
21475 e.preventDefault();
21480 Roo.bootstrap.DateField.superclass.keyup.call(this);
21484 setValue: function(v)
21486 if(this.fireEvent('beforeselect', this, v) !== false){
21487 var d = new Date(this.parseDate(v) ).clearTime();
21489 if(isNaN(d.getTime())){
21490 this.date = this.viewDate = '';
21491 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21495 v = this.formatDate(d);
21497 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21499 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21503 this.fireEvent('select', this, this.date);
21507 getValue: function()
21509 return this.formatDate(this.date);
21512 fireKey: function(e)
21514 if (!this.picker().isVisible()){
21515 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21521 var dateChanged = false,
21523 newDate, newViewDate;
21528 e.preventDefault();
21532 if (!this.keyboardNavigation) {
21535 dir = e.keyCode == 37 ? -1 : 1;
21538 newDate = this.moveYear(this.date, dir);
21539 newViewDate = this.moveYear(this.viewDate, dir);
21540 } else if (e.shiftKey){
21541 newDate = this.moveMonth(this.date, dir);
21542 newViewDate = this.moveMonth(this.viewDate, dir);
21544 newDate = new Date(this.date);
21545 newDate.setUTCDate(this.date.getUTCDate() + dir);
21546 newViewDate = new Date(this.viewDate);
21547 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21549 if (this.dateWithinRange(newDate)){
21550 this.date = newDate;
21551 this.viewDate = newViewDate;
21552 this.setValue(this.formatDate(this.date));
21554 e.preventDefault();
21555 dateChanged = true;
21560 if (!this.keyboardNavigation) {
21563 dir = e.keyCode == 38 ? -1 : 1;
21565 newDate = this.moveYear(this.date, dir);
21566 newViewDate = this.moveYear(this.viewDate, dir);
21567 } else if (e.shiftKey){
21568 newDate = this.moveMonth(this.date, dir);
21569 newViewDate = this.moveMonth(this.viewDate, dir);
21571 newDate = new Date(this.date);
21572 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21573 newViewDate = new Date(this.viewDate);
21574 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21576 if (this.dateWithinRange(newDate)){
21577 this.date = newDate;
21578 this.viewDate = newViewDate;
21579 this.setValue(this.formatDate(this.date));
21581 e.preventDefault();
21582 dateChanged = true;
21586 this.setValue(this.formatDate(this.date));
21588 e.preventDefault();
21591 this.setValue(this.formatDate(this.date));
21605 onClick: function(e)
21607 e.stopPropagation();
21608 e.preventDefault();
21610 var target = e.getTarget();
21612 if(target.nodeName.toLowerCase() === 'i'){
21613 target = Roo.get(target).dom.parentNode;
21616 var nodeName = target.nodeName;
21617 var className = target.className;
21618 var html = target.innerHTML;
21619 //Roo.log(nodeName);
21621 switch(nodeName.toLowerCase()) {
21623 switch(className) {
21629 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21630 switch(this.viewMode){
21632 this.viewDate = this.moveMonth(this.viewDate, dir);
21636 this.viewDate = this.moveYear(this.viewDate, dir);
21642 var date = new Date();
21643 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21645 this.setValue(this.formatDate(this.date));
21652 if (className.indexOf('disabled') < 0) {
21653 this.viewDate.setUTCDate(1);
21654 if (className.indexOf('month') > -1) {
21655 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21657 var year = parseInt(html, 10) || 0;
21658 this.viewDate.setUTCFullYear(year);
21662 if(this.singleMode){
21663 this.setValue(this.formatDate(this.viewDate));
21674 //Roo.log(className);
21675 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21676 var day = parseInt(html, 10) || 1;
21677 var year = (this.viewDate || new Date()).getUTCFullYear(),
21678 month = (this.viewDate || new Date()).getUTCMonth();
21680 if (className.indexOf('old') > -1) {
21687 } else if (className.indexOf('new') > -1) {
21695 //Roo.log([year,month,day]);
21696 this.date = this.UTCDate(year, month, day,0,0,0,0);
21697 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21699 //Roo.log(this.formatDate(this.date));
21700 this.setValue(this.formatDate(this.date));
21707 setStartDate: function(startDate)
21709 this.startDate = startDate || -Infinity;
21710 if (this.startDate !== -Infinity) {
21711 this.startDate = this.parseDate(this.startDate);
21714 this.updateNavArrows();
21717 setEndDate: function(endDate)
21719 this.endDate = endDate || Infinity;
21720 if (this.endDate !== Infinity) {
21721 this.endDate = this.parseDate(this.endDate);
21724 this.updateNavArrows();
21727 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21729 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21730 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21731 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21733 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21734 return parseInt(d, 10);
21737 this.updateNavArrows();
21740 updateNavArrows: function()
21742 if(this.singleMode){
21746 var d = new Date(this.viewDate),
21747 year = d.getUTCFullYear(),
21748 month = d.getUTCMonth();
21750 Roo.each(this.picker().select('.prev', true).elements, function(v){
21752 switch (this.viewMode) {
21755 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21761 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21768 Roo.each(this.picker().select('.next', true).elements, function(v){
21770 switch (this.viewMode) {
21773 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21779 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21787 moveMonth: function(date, dir)
21792 var new_date = new Date(date.valueOf()),
21793 day = new_date.getUTCDate(),
21794 month = new_date.getUTCMonth(),
21795 mag = Math.abs(dir),
21797 dir = dir > 0 ? 1 : -1;
21800 // If going back one month, make sure month is not current month
21801 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21803 return new_date.getUTCMonth() == month;
21805 // If going forward one month, make sure month is as expected
21806 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21808 return new_date.getUTCMonth() != new_month;
21810 new_month = month + dir;
21811 new_date.setUTCMonth(new_month);
21812 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21813 if (new_month < 0 || new_month > 11) {
21814 new_month = (new_month + 12) % 12;
21817 // For magnitudes >1, move one month at a time...
21818 for (var i=0; i<mag; i++) {
21819 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21820 new_date = this.moveMonth(new_date, dir);
21822 // ...then reset the day, keeping it in the new month
21823 new_month = new_date.getUTCMonth();
21824 new_date.setUTCDate(day);
21826 return new_month != new_date.getUTCMonth();
21829 // Common date-resetting loop -- if date is beyond end of month, make it
21832 new_date.setUTCDate(--day);
21833 new_date.setUTCMonth(new_month);
21838 moveYear: function(date, dir)
21840 return this.moveMonth(date, dir*12);
21843 dateWithinRange: function(date)
21845 return date >= this.startDate && date <= this.endDate;
21851 this.picker().remove();
21854 validateValue : function(value)
21856 if(this.getVisibilityEl().hasClass('hidden')){
21860 if(value.length < 1) {
21861 if(this.allowBlank){
21867 if(value.length < this.minLength){
21870 if(value.length > this.maxLength){
21874 var vt = Roo.form.VTypes;
21875 if(!vt[this.vtype](value, this)){
21879 if(typeof this.validator == "function"){
21880 var msg = this.validator(value);
21886 if(this.regex && !this.regex.test(value)){
21890 if(typeof(this.parseDate(value)) == 'undefined'){
21894 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21898 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21908 this.date = this.viewDate = '';
21910 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21915 Roo.apply(Roo.bootstrap.DateField, {
21926 html: '<i class="fa fa-arrow-left"/>'
21936 html: '<i class="fa fa-arrow-right"/>'
21978 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21979 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21980 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21981 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21982 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21995 navFnc: 'FullYear',
22000 navFnc: 'FullYear',
22005 Roo.apply(Roo.bootstrap.DateField, {
22009 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22013 cls: 'datepicker-days',
22017 cls: 'table-condensed',
22019 Roo.bootstrap.DateField.head,
22023 Roo.bootstrap.DateField.footer
22030 cls: 'datepicker-months',
22034 cls: 'table-condensed',
22036 Roo.bootstrap.DateField.head,
22037 Roo.bootstrap.DateField.content,
22038 Roo.bootstrap.DateField.footer
22045 cls: 'datepicker-years',
22049 cls: 'table-condensed',
22051 Roo.bootstrap.DateField.head,
22052 Roo.bootstrap.DateField.content,
22053 Roo.bootstrap.DateField.footer
22072 * @class Roo.bootstrap.TimeField
22073 * @extends Roo.bootstrap.Input
22074 * Bootstrap DateField class
22078 * Create a new TimeField
22079 * @param {Object} config The config object
22082 Roo.bootstrap.TimeField = function(config){
22083 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22087 * Fires when this field show.
22088 * @param {Roo.bootstrap.DateField} thisthis
22089 * @param {Mixed} date The date value
22094 * Fires when this field hide.
22095 * @param {Roo.bootstrap.DateField} this
22096 * @param {Mixed} date The date value
22101 * Fires when select a date.
22102 * @param {Roo.bootstrap.DateField} this
22103 * @param {Mixed} date The date value
22109 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22112 * @cfg {String} format
22113 * The default time format string which can be overriden for localization support. The format must be
22114 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22118 getAutoCreate : function()
22120 this.after = '<i class="fa far fa-clock"></i>';
22121 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22125 onRender: function(ct, position)
22128 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22130 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22132 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22134 this.pop = this.picker().select('>.datepicker-time',true).first();
22135 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22137 this.picker().on('mousedown', this.onMousedown, this);
22138 this.picker().on('click', this.onClick, this);
22140 this.picker().addClass('datepicker-dropdown');
22145 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22146 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22147 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22148 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22149 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22150 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22154 fireKey: function(e){
22155 if (!this.picker().isVisible()){
22156 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22162 e.preventDefault();
22170 this.onTogglePeriod();
22173 this.onIncrementMinutes();
22176 this.onDecrementMinutes();
22185 onClick: function(e) {
22186 e.stopPropagation();
22187 e.preventDefault();
22190 picker : function()
22192 return this.pickerEl;
22195 fillTime: function()
22197 var time = this.pop.select('tbody', true).first();
22199 time.dom.innerHTML = '';
22214 cls: 'hours-up fa fas fa-chevron-up'
22234 cls: 'minutes-up fa fas fa-chevron-up'
22255 cls: 'timepicker-hour',
22270 cls: 'timepicker-minute',
22285 cls: 'btn btn-primary period',
22307 cls: 'hours-down fa fas fa-chevron-down'
22327 cls: 'minutes-down fa fas fa-chevron-down'
22345 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22352 var hours = this.time.getHours();
22353 var minutes = this.time.getMinutes();
22366 hours = hours - 12;
22370 hours = '0' + hours;
22374 minutes = '0' + minutes;
22377 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22378 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22379 this.pop.select('button', true).first().dom.innerHTML = period;
22385 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22387 var cls = ['bottom'];
22389 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22396 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22400 //this.picker().setXY(20000,20000);
22401 this.picker().addClass(cls.join('-'));
22405 Roo.each(cls, function(c){
22410 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22411 //_this.picker().setTop(_this.inputEl().getHeight());
22415 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22417 //_this.picker().setTop(0 - _this.picker().getHeight());
22422 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22426 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22434 onFocus : function()
22436 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22440 onBlur : function()
22442 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22448 this.picker().show();
22453 this.fireEvent('show', this, this.date);
22458 this.picker().hide();
22461 this.fireEvent('hide', this, this.date);
22464 setTime : function()
22467 this.setValue(this.time.format(this.format));
22469 this.fireEvent('select', this, this.date);
22474 onMousedown: function(e){
22475 e.stopPropagation();
22476 e.preventDefault();
22479 onIncrementHours: function()
22481 Roo.log('onIncrementHours');
22482 this.time = this.time.add(Date.HOUR, 1);
22487 onDecrementHours: function()
22489 Roo.log('onDecrementHours');
22490 this.time = this.time.add(Date.HOUR, -1);
22494 onIncrementMinutes: function()
22496 Roo.log('onIncrementMinutes');
22497 this.time = this.time.add(Date.MINUTE, 1);
22501 onDecrementMinutes: function()
22503 Roo.log('onDecrementMinutes');
22504 this.time = this.time.add(Date.MINUTE, -1);
22508 onTogglePeriod: function()
22510 Roo.log('onTogglePeriod');
22511 this.time = this.time.add(Date.HOUR, 12);
22519 Roo.apply(Roo.bootstrap.TimeField, {
22523 cls: 'datepicker dropdown-menu',
22527 cls: 'datepicker-time',
22531 cls: 'table-condensed',
22560 cls: 'btn btn-info ok',
22588 * @class Roo.bootstrap.MonthField
22589 * @extends Roo.bootstrap.Input
22590 * Bootstrap MonthField class
22592 * @cfg {String} language default en
22595 * Create a new MonthField
22596 * @param {Object} config The config object
22599 Roo.bootstrap.MonthField = function(config){
22600 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22605 * Fires when this field show.
22606 * @param {Roo.bootstrap.MonthField} this
22607 * @param {Mixed} date The date value
22612 * Fires when this field hide.
22613 * @param {Roo.bootstrap.MonthField} this
22614 * @param {Mixed} date The date value
22619 * Fires when select a date.
22620 * @param {Roo.bootstrap.MonthField} this
22621 * @param {String} oldvalue The old value
22622 * @param {String} newvalue The new value
22628 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22630 onRender: function(ct, position)
22633 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22635 this.language = this.language || 'en';
22636 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22637 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22639 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22640 this.isInline = false;
22641 this.isInput = true;
22642 this.component = this.el.select('.add-on', true).first() || false;
22643 this.component = (this.component && this.component.length === 0) ? false : this.component;
22644 this.hasInput = this.component && this.inputEL().length;
22646 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22648 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22650 this.picker().on('mousedown', this.onMousedown, this);
22651 this.picker().on('click', this.onClick, this);
22653 this.picker().addClass('datepicker-dropdown');
22655 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22656 v.setStyle('width', '189px');
22663 if(this.isInline) {
22669 setValue: function(v, suppressEvent)
22671 var o = this.getValue();
22673 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22677 if(suppressEvent !== true){
22678 this.fireEvent('select', this, o, v);
22683 getValue: function()
22688 onClick: function(e)
22690 e.stopPropagation();
22691 e.preventDefault();
22693 var target = e.getTarget();
22695 if(target.nodeName.toLowerCase() === 'i'){
22696 target = Roo.get(target).dom.parentNode;
22699 var nodeName = target.nodeName;
22700 var className = target.className;
22701 var html = target.innerHTML;
22703 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22707 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22709 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22715 picker : function()
22717 return this.pickerEl;
22720 fillMonths: function()
22723 var months = this.picker().select('>.datepicker-months td', true).first();
22725 months.dom.innerHTML = '';
22731 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22734 months.createChild(month);
22743 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22744 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22747 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22748 e.removeClass('active');
22750 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22751 e.addClass('active');
22758 if(this.isInline) {
22762 this.picker().removeClass(['bottom', 'top']);
22764 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22766 * place to the top of element!
22770 this.picker().addClass('top');
22771 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22776 this.picker().addClass('bottom');
22778 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22781 onFocus : function()
22783 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22787 onBlur : function()
22789 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22791 var d = this.inputEl().getValue();
22800 this.picker().show();
22801 this.picker().select('>.datepicker-months', true).first().show();
22805 this.fireEvent('show', this, this.date);
22810 if(this.isInline) {
22813 this.picker().hide();
22814 this.fireEvent('hide', this, this.date);
22818 onMousedown: function(e)
22820 e.stopPropagation();
22821 e.preventDefault();
22826 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22830 fireKey: function(e)
22832 if (!this.picker().isVisible()){
22833 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22844 e.preventDefault();
22848 dir = e.keyCode == 37 ? -1 : 1;
22850 this.vIndex = this.vIndex + dir;
22852 if(this.vIndex < 0){
22856 if(this.vIndex > 11){
22860 if(isNaN(this.vIndex)){
22864 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22870 dir = e.keyCode == 38 ? -1 : 1;
22872 this.vIndex = this.vIndex + dir * 4;
22874 if(this.vIndex < 0){
22878 if(this.vIndex > 11){
22882 if(isNaN(this.vIndex)){
22886 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22891 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22892 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22896 e.preventDefault();
22899 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22900 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22916 this.picker().remove();
22921 Roo.apply(Roo.bootstrap.MonthField, {
22940 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22941 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22946 Roo.apply(Roo.bootstrap.MonthField, {
22950 cls: 'datepicker dropdown-menu roo-dynamic',
22954 cls: 'datepicker-months',
22958 cls: 'table-condensed',
22960 Roo.bootstrap.DateField.content
22980 * @class Roo.bootstrap.CheckBox
22981 * @extends Roo.bootstrap.Input
22982 * Bootstrap CheckBox class
22984 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22985 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22986 * @cfg {String} boxLabel The text that appears beside the checkbox
22987 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22988 * @cfg {Boolean} checked initnal the element
22989 * @cfg {Boolean} inline inline the element (default false)
22990 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22991 * @cfg {String} tooltip label tooltip
22994 * Create a new CheckBox
22995 * @param {Object} config The config object
22998 Roo.bootstrap.CheckBox = function(config){
22999 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23004 * Fires when the element is checked or unchecked.
23005 * @param {Roo.bootstrap.CheckBox} this This input
23006 * @param {Boolean} checked The new checked value
23011 * Fires when the element is click.
23012 * @param {Roo.bootstrap.CheckBox} this This input
23019 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23021 inputType: 'checkbox',
23030 // checkbox success does not make any sense really..
23035 getAutoCreate : function()
23037 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23043 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23046 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23052 type : this.inputType,
23053 value : this.inputValue,
23054 cls : 'roo-' + this.inputType, //'form-box',
23055 placeholder : this.placeholder || ''
23059 if(this.inputType != 'radio'){
23063 cls : 'roo-hidden-value',
23064 value : this.checked ? this.inputValue : this.valueOff
23069 if (this.weight) { // Validity check?
23070 cfg.cls += " " + this.inputType + "-" + this.weight;
23073 if (this.disabled) {
23074 input.disabled=true;
23078 input.checked = this.checked;
23083 input.name = this.name;
23085 if(this.inputType != 'radio'){
23086 hidden.name = this.name;
23087 input.name = '_hidden_' + this.name;
23092 input.cls += ' input-' + this.size;
23097 ['xs','sm','md','lg'].map(function(size){
23098 if (settings[size]) {
23099 cfg.cls += ' col-' + size + '-' + settings[size];
23103 var inputblock = input;
23105 if (this.before || this.after) {
23108 cls : 'input-group',
23113 inputblock.cn.push({
23115 cls : 'input-group-addon',
23120 inputblock.cn.push(input);
23122 if(this.inputType != 'radio'){
23123 inputblock.cn.push(hidden);
23127 inputblock.cn.push({
23129 cls : 'input-group-addon',
23135 var boxLabelCfg = false;
23141 //'for': id, // box label is handled by onclick - so no for...
23143 html: this.boxLabel
23146 boxLabelCfg.tooltip = this.tooltip;
23152 if (align ==='left' && this.fieldLabel.length) {
23153 // Roo.log("left and has label");
23158 cls : 'control-label',
23159 html : this.fieldLabel
23170 cfg.cn[1].cn.push(boxLabelCfg);
23173 if(this.labelWidth > 12){
23174 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23177 if(this.labelWidth < 13 && this.labelmd == 0){
23178 this.labelmd = this.labelWidth;
23181 if(this.labellg > 0){
23182 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23183 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23186 if(this.labelmd > 0){
23187 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23188 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23191 if(this.labelsm > 0){
23192 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23193 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23196 if(this.labelxs > 0){
23197 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23198 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23201 } else if ( this.fieldLabel.length) {
23202 // Roo.log(" label");
23206 tag: this.boxLabel ? 'span' : 'label',
23208 cls: 'control-label box-input-label',
23209 //cls : 'input-group-addon',
23210 html : this.fieldLabel
23217 cfg.cn.push(boxLabelCfg);
23222 // Roo.log(" no label && no align");
23223 cfg.cn = [ inputblock ] ;
23225 cfg.cn.push(boxLabelCfg);
23233 if(this.inputType != 'radio'){
23234 cfg.cn.push(hidden);
23242 * return the real input element.
23244 inputEl: function ()
23246 return this.el.select('input.roo-' + this.inputType,true).first();
23248 hiddenEl: function ()
23250 return this.el.select('input.roo-hidden-value',true).first();
23253 labelEl: function()
23255 return this.el.select('label.control-label',true).first();
23257 /* depricated... */
23261 return this.labelEl();
23264 boxLabelEl: function()
23266 return this.el.select('label.box-label',true).first();
23269 initEvents : function()
23271 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23273 this.inputEl().on('click', this.onClick, this);
23275 if (this.boxLabel) {
23276 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23279 this.startValue = this.getValue();
23282 Roo.bootstrap.CheckBox.register(this);
23286 onClick : function(e)
23288 if(this.fireEvent('click', this, e) !== false){
23289 this.setChecked(!this.checked);
23294 setChecked : function(state,suppressEvent)
23296 this.startValue = this.getValue();
23298 if(this.inputType == 'radio'){
23300 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23301 e.dom.checked = false;
23304 this.inputEl().dom.checked = true;
23306 this.inputEl().dom.value = this.inputValue;
23308 if(suppressEvent !== true){
23309 this.fireEvent('check', this, true);
23317 this.checked = state;
23319 this.inputEl().dom.checked = state;
23322 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23324 if(suppressEvent !== true){
23325 this.fireEvent('check', this, state);
23331 getValue : function()
23333 if(this.inputType == 'radio'){
23334 return this.getGroupValue();
23337 return this.hiddenEl().dom.value;
23341 getGroupValue : function()
23343 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23347 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23350 setValue : function(v,suppressEvent)
23352 if(this.inputType == 'radio'){
23353 this.setGroupValue(v, suppressEvent);
23357 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23362 setGroupValue : function(v, suppressEvent)
23364 this.startValue = this.getValue();
23366 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23367 e.dom.checked = false;
23369 if(e.dom.value == v){
23370 e.dom.checked = true;
23374 if(suppressEvent !== true){
23375 this.fireEvent('check', this, true);
23383 validate : function()
23385 if(this.getVisibilityEl().hasClass('hidden')){
23391 (this.inputType == 'radio' && this.validateRadio()) ||
23392 (this.inputType == 'checkbox' && this.validateCheckbox())
23398 this.markInvalid();
23402 validateRadio : function()
23404 if(this.getVisibilityEl().hasClass('hidden')){
23408 if(this.allowBlank){
23414 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23415 if(!e.dom.checked){
23427 validateCheckbox : function()
23430 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23431 //return (this.getValue() == this.inputValue) ? true : false;
23434 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23442 for(var i in group){
23443 if(group[i].el.isVisible(true)){
23451 for(var i in group){
23456 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23463 * Mark this field as valid
23465 markValid : function()
23469 this.fireEvent('valid', this);
23471 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23474 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23481 if(this.inputType == 'radio'){
23482 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23483 var fg = e.findParent('.form-group', false, true);
23484 if (Roo.bootstrap.version == 3) {
23485 fg.removeClass([_this.invalidClass, _this.validClass]);
23486 fg.addClass(_this.validClass);
23488 fg.removeClass(['is-valid', 'is-invalid']);
23489 fg.addClass('is-valid');
23497 var fg = this.el.findParent('.form-group', false, true);
23498 if (Roo.bootstrap.version == 3) {
23499 fg.removeClass([this.invalidClass, this.validClass]);
23500 fg.addClass(this.validClass);
23502 fg.removeClass(['is-valid', 'is-invalid']);
23503 fg.addClass('is-valid');
23508 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23514 for(var i in group){
23515 var fg = group[i].el.findParent('.form-group', false, true);
23516 if (Roo.bootstrap.version == 3) {
23517 fg.removeClass([this.invalidClass, this.validClass]);
23518 fg.addClass(this.validClass);
23520 fg.removeClass(['is-valid', 'is-invalid']);
23521 fg.addClass('is-valid');
23527 * Mark this field as invalid
23528 * @param {String} msg The validation message
23530 markInvalid : function(msg)
23532 if(this.allowBlank){
23538 this.fireEvent('invalid', this, msg);
23540 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23543 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23547 label.markInvalid();
23550 if(this.inputType == 'radio'){
23552 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23553 var fg = e.findParent('.form-group', false, true);
23554 if (Roo.bootstrap.version == 3) {
23555 fg.removeClass([_this.invalidClass, _this.validClass]);
23556 fg.addClass(_this.invalidClass);
23558 fg.removeClass(['is-invalid', 'is-valid']);
23559 fg.addClass('is-invalid');
23567 var fg = this.el.findParent('.form-group', false, true);
23568 if (Roo.bootstrap.version == 3) {
23569 fg.removeClass([_this.invalidClass, _this.validClass]);
23570 fg.addClass(_this.invalidClass);
23572 fg.removeClass(['is-invalid', 'is-valid']);
23573 fg.addClass('is-invalid');
23578 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23584 for(var i in group){
23585 var fg = group[i].el.findParent('.form-group', false, true);
23586 if (Roo.bootstrap.version == 3) {
23587 fg.removeClass([_this.invalidClass, _this.validClass]);
23588 fg.addClass(_this.invalidClass);
23590 fg.removeClass(['is-invalid', 'is-valid']);
23591 fg.addClass('is-invalid');
23597 clearInvalid : function()
23599 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23601 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23603 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23605 if (label && label.iconEl) {
23606 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23607 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23611 disable : function()
23613 if(this.inputType != 'radio'){
23614 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23621 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23622 _this.getActionEl().addClass(this.disabledClass);
23623 e.dom.disabled = true;
23627 this.disabled = true;
23628 this.fireEvent("disable", this);
23632 enable : function()
23634 if(this.inputType != 'radio'){
23635 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23642 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23643 _this.getActionEl().removeClass(this.disabledClass);
23644 e.dom.disabled = false;
23648 this.disabled = false;
23649 this.fireEvent("enable", this);
23653 setBoxLabel : function(v)
23658 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23664 Roo.apply(Roo.bootstrap.CheckBox, {
23669 * register a CheckBox Group
23670 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23672 register : function(checkbox)
23674 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23675 this.groups[checkbox.groupId] = {};
23678 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23682 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23686 * fetch a CheckBox Group based on the group ID
23687 * @param {string} the group ID
23688 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23690 get: function(groupId) {
23691 if (typeof(this.groups[groupId]) == 'undefined') {
23695 return this.groups[groupId] ;
23708 * @class Roo.bootstrap.Radio
23709 * @extends Roo.bootstrap.Component
23710 * Bootstrap Radio class
23711 * @cfg {String} boxLabel - the label associated
23712 * @cfg {String} value - the value of radio
23715 * Create a new Radio
23716 * @param {Object} config The config object
23718 Roo.bootstrap.Radio = function(config){
23719 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23723 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23729 getAutoCreate : function()
23733 cls : 'form-group radio',
23738 html : this.boxLabel
23746 initEvents : function()
23748 this.parent().register(this);
23750 this.el.on('click', this.onClick, this);
23754 onClick : function(e)
23756 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23757 this.setChecked(true);
23761 setChecked : function(state, suppressEvent)
23763 this.parent().setValue(this.value, suppressEvent);
23767 setBoxLabel : function(v)
23772 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23787 * @class Roo.bootstrap.SecurePass
23788 * @extends Roo.bootstrap.Input
23789 * Bootstrap SecurePass class
23793 * Create a new SecurePass
23794 * @param {Object} config The config object
23797 Roo.bootstrap.SecurePass = function (config) {
23798 // these go here, so the translation tool can replace them..
23800 PwdEmpty: "Please type a password, and then retype it to confirm.",
23801 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23802 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23803 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23804 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23805 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23806 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23807 TooWeak: "Your password is Too Weak."
23809 this.meterLabel = "Password strength:";
23810 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23811 this.meterClass = [
23812 "roo-password-meter-tooweak",
23813 "roo-password-meter-weak",
23814 "roo-password-meter-medium",
23815 "roo-password-meter-strong",
23816 "roo-password-meter-grey"
23821 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23824 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23826 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23828 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23829 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23830 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23831 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23832 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23833 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23834 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23844 * @cfg {String/Object} Label for the strength meter (defaults to
23845 * 'Password strength:')
23850 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23851 * ['Weak', 'Medium', 'Strong'])
23854 pwdStrengths: false,
23867 initEvents: function ()
23869 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23871 if (this.el.is('input[type=password]') && Roo.isSafari) {
23872 this.el.on('keydown', this.SafariOnKeyDown, this);
23875 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23878 onRender: function (ct, position)
23880 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23881 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23882 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23884 this.trigger.createChild({
23889 cls: 'roo-password-meter-grey col-xs-12',
23892 //width: this.meterWidth + 'px'
23896 cls: 'roo-password-meter-text'
23902 if (this.hideTrigger) {
23903 this.trigger.setDisplayed(false);
23905 this.setSize(this.width || '', this.height || '');
23908 onDestroy: function ()
23910 if (this.trigger) {
23911 this.trigger.removeAllListeners();
23912 this.trigger.remove();
23915 this.wrap.remove();
23917 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23920 checkStrength: function ()
23922 var pwd = this.inputEl().getValue();
23923 if (pwd == this._lastPwd) {
23928 if (this.ClientSideStrongPassword(pwd)) {
23930 } else if (this.ClientSideMediumPassword(pwd)) {
23932 } else if (this.ClientSideWeakPassword(pwd)) {
23938 Roo.log('strength1: ' + strength);
23940 //var pm = this.trigger.child('div/div/div').dom;
23941 var pm = this.trigger.child('div/div');
23942 pm.removeClass(this.meterClass);
23943 pm.addClass(this.meterClass[strength]);
23946 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23948 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23950 this._lastPwd = pwd;
23954 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23956 this._lastPwd = '';
23958 var pm = this.trigger.child('div/div');
23959 pm.removeClass(this.meterClass);
23960 pm.addClass('roo-password-meter-grey');
23963 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23966 this.inputEl().dom.type='password';
23969 validateValue: function (value)
23971 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23974 if (value.length == 0) {
23975 if (this.allowBlank) {
23976 this.clearInvalid();
23980 this.markInvalid(this.errors.PwdEmpty);
23981 this.errorMsg = this.errors.PwdEmpty;
23989 if (!value.match(/[\x21-\x7e]+/)) {
23990 this.markInvalid(this.errors.PwdBadChar);
23991 this.errorMsg = this.errors.PwdBadChar;
23994 if (value.length < 6) {
23995 this.markInvalid(this.errors.PwdShort);
23996 this.errorMsg = this.errors.PwdShort;
23999 if (value.length > 16) {
24000 this.markInvalid(this.errors.PwdLong);
24001 this.errorMsg = this.errors.PwdLong;
24005 if (this.ClientSideStrongPassword(value)) {
24007 } else if (this.ClientSideMediumPassword(value)) {
24009 } else if (this.ClientSideWeakPassword(value)) {
24016 if (strength < 2) {
24017 //this.markInvalid(this.errors.TooWeak);
24018 this.errorMsg = this.errors.TooWeak;
24023 console.log('strength2: ' + strength);
24025 //var pm = this.trigger.child('div/div/div').dom;
24027 var pm = this.trigger.child('div/div');
24028 pm.removeClass(this.meterClass);
24029 pm.addClass(this.meterClass[strength]);
24031 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24033 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24035 this.errorMsg = '';
24039 CharacterSetChecks: function (type)
24042 this.fResult = false;
24045 isctype: function (character, type)
24048 case this.kCapitalLetter:
24049 if (character >= 'A' && character <= 'Z') {
24054 case this.kSmallLetter:
24055 if (character >= 'a' && character <= 'z') {
24061 if (character >= '0' && character <= '9') {
24066 case this.kPunctuation:
24067 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24078 IsLongEnough: function (pwd, size)
24080 return !(pwd == null || isNaN(size) || pwd.length < size);
24083 SpansEnoughCharacterSets: function (word, nb)
24085 if (!this.IsLongEnough(word, nb))
24090 var characterSetChecks = new Array(
24091 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24092 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24095 for (var index = 0; index < word.length; ++index) {
24096 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24097 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24098 characterSetChecks[nCharSet].fResult = true;
24105 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24106 if (characterSetChecks[nCharSet].fResult) {
24111 if (nCharSets < nb) {
24117 ClientSideStrongPassword: function (pwd)
24119 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24122 ClientSideMediumPassword: function (pwd)
24124 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24127 ClientSideWeakPassword: function (pwd)
24129 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24132 })//<script type="text/javascript">
24135 * Based Ext JS Library 1.1.1
24136 * Copyright(c) 2006-2007, Ext JS, LLC.
24142 * @class Roo.HtmlEditorCore
24143 * @extends Roo.Component
24144 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24146 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24149 Roo.HtmlEditorCore = function(config){
24152 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24157 * @event initialize
24158 * Fires when the editor is fully initialized (including the iframe)
24159 * @param {Roo.HtmlEditorCore} this
24164 * Fires when the editor is first receives the focus. Any insertion must wait
24165 * until after this event.
24166 * @param {Roo.HtmlEditorCore} this
24170 * @event beforesync
24171 * Fires before the textarea is updated with content from the editor iframe. Return false
24172 * to cancel the sync.
24173 * @param {Roo.HtmlEditorCore} this
24174 * @param {String} html
24178 * @event beforepush
24179 * Fires before the iframe editor is updated with content from the textarea. Return false
24180 * to cancel the push.
24181 * @param {Roo.HtmlEditorCore} this
24182 * @param {String} html
24187 * Fires when the textarea is updated with content from the editor iframe.
24188 * @param {Roo.HtmlEditorCore} this
24189 * @param {String} html
24194 * Fires when the iframe editor is updated with content from the textarea.
24195 * @param {Roo.HtmlEditorCore} this
24196 * @param {String} html
24201 * @event editorevent
24202 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24203 * @param {Roo.HtmlEditorCore} this
24209 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24211 // defaults : white / black...
24212 this.applyBlacklists();
24219 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24223 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24229 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24234 * @cfg {Number} height (in pixels)
24238 * @cfg {Number} width (in pixels)
24243 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24246 stylesheets: false,
24251 // private properties
24252 validationEvent : false,
24254 initialized : false,
24256 sourceEditMode : false,
24257 onFocus : Roo.emptyFn,
24259 hideMode:'offsets',
24263 // blacklist + whitelisted elements..
24270 * Protected method that will not generally be called directly. It
24271 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24272 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24274 getDocMarkup : function(){
24278 // inherit styels from page...??
24279 if (this.stylesheets === false) {
24281 Roo.get(document.head).select('style').each(function(node) {
24282 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24285 Roo.get(document.head).select('link').each(function(node) {
24286 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24289 } else if (!this.stylesheets.length) {
24291 st = '<style type="text/css">' +
24292 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24295 for (var i in this.stylesheets) {
24296 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24301 st += '<style type="text/css">' +
24302 'IMG { cursor: pointer } ' +
24305 var cls = 'roo-htmleditor-body';
24307 if(this.bodyCls.length){
24308 cls += ' ' + this.bodyCls;
24311 return '<html><head>' + st +
24312 //<style type="text/css">' +
24313 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24315 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24319 onRender : function(ct, position)
24322 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24323 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24326 this.el.dom.style.border = '0 none';
24327 this.el.dom.setAttribute('tabIndex', -1);
24328 this.el.addClass('x-hidden hide');
24332 if(Roo.isIE){ // fix IE 1px bogus margin
24333 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24337 this.frameId = Roo.id();
24341 var iframe = this.owner.wrap.createChild({
24343 cls: 'form-control', // bootstrap..
24345 name: this.frameId,
24346 frameBorder : 'no',
24347 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24352 this.iframe = iframe.dom;
24354 this.assignDocWin();
24356 this.doc.designMode = 'on';
24359 this.doc.write(this.getDocMarkup());
24363 var task = { // must defer to wait for browser to be ready
24365 //console.log("run task?" + this.doc.readyState);
24366 this.assignDocWin();
24367 if(this.doc.body || this.doc.readyState == 'complete'){
24369 this.doc.designMode="on";
24373 Roo.TaskMgr.stop(task);
24374 this.initEditor.defer(10, this);
24381 Roo.TaskMgr.start(task);
24386 onResize : function(w, h)
24388 Roo.log('resize: ' +w + ',' + h );
24389 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24393 if(typeof w == 'number'){
24395 this.iframe.style.width = w + 'px';
24397 if(typeof h == 'number'){
24399 this.iframe.style.height = h + 'px';
24401 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24408 * Toggles the editor between standard and source edit mode.
24409 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24411 toggleSourceEdit : function(sourceEditMode){
24413 this.sourceEditMode = sourceEditMode === true;
24415 if(this.sourceEditMode){
24417 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24420 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24421 //this.iframe.className = '';
24424 //this.setSize(this.owner.wrap.getSize());
24425 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24432 * Protected method that will not generally be called directly. If you need/want
24433 * custom HTML cleanup, this is the method you should override.
24434 * @param {String} html The HTML to be cleaned
24435 * return {String} The cleaned HTML
24437 cleanHtml : function(html){
24438 html = String(html);
24439 if(html.length > 5){
24440 if(Roo.isSafari){ // strip safari nonsense
24441 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24444 if(html == ' '){
24451 * HTML Editor -> Textarea
24452 * Protected method that will not generally be called directly. Syncs the contents
24453 * of the editor iframe with the textarea.
24455 syncValue : function(){
24456 if(this.initialized){
24457 var bd = (this.doc.body || this.doc.documentElement);
24458 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24459 var html = bd.innerHTML;
24461 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24462 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24464 html = '<div style="'+m[0]+'">' + html + '</div>';
24467 html = this.cleanHtml(html);
24468 // fix up the special chars.. normaly like back quotes in word...
24469 // however we do not want to do this with chinese..
24470 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24472 var cc = match.charCodeAt();
24474 // Get the character value, handling surrogate pairs
24475 if (match.length == 2) {
24476 // It's a surrogate pair, calculate the Unicode code point
24477 var high = match.charCodeAt(0) - 0xD800;
24478 var low = match.charCodeAt(1) - 0xDC00;
24479 cc = (high * 0x400) + low + 0x10000;
24481 (cc >= 0x4E00 && cc < 0xA000 ) ||
24482 (cc >= 0x3400 && cc < 0x4E00 ) ||
24483 (cc >= 0xf900 && cc < 0xfb00 )
24488 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24489 return "&#" + cc + ";";
24496 if(this.owner.fireEvent('beforesync', this, html) !== false){
24497 this.el.dom.value = html;
24498 this.owner.fireEvent('sync', this, html);
24504 * Protected method that will not generally be called directly. Pushes the value of the textarea
24505 * into the iframe editor.
24507 pushValue : function(){
24508 if(this.initialized){
24509 var v = this.el.dom.value.trim();
24511 // if(v.length < 1){
24515 if(this.owner.fireEvent('beforepush', this, v) !== false){
24516 var d = (this.doc.body || this.doc.documentElement);
24518 this.cleanUpPaste();
24519 this.el.dom.value = d.innerHTML;
24520 this.owner.fireEvent('push', this, v);
24526 deferFocus : function(){
24527 this.focus.defer(10, this);
24531 focus : function(){
24532 if(this.win && !this.sourceEditMode){
24539 assignDocWin: function()
24541 var iframe = this.iframe;
24544 this.doc = iframe.contentWindow.document;
24545 this.win = iframe.contentWindow;
24547 // if (!Roo.get(this.frameId)) {
24550 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24551 // this.win = Roo.get(this.frameId).dom.contentWindow;
24553 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24557 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24558 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24563 initEditor : function(){
24564 //console.log("INIT EDITOR");
24565 this.assignDocWin();
24569 this.doc.designMode="on";
24571 this.doc.write(this.getDocMarkup());
24574 var dbody = (this.doc.body || this.doc.documentElement);
24575 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24576 // this copies styles from the containing element into thsi one..
24577 // not sure why we need all of this..
24578 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24580 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24581 //ss['background-attachment'] = 'fixed'; // w3c
24582 dbody.bgProperties = 'fixed'; // ie
24583 //Roo.DomHelper.applyStyles(dbody, ss);
24584 Roo.EventManager.on(this.doc, {
24585 //'mousedown': this.onEditorEvent,
24586 'mouseup': this.onEditorEvent,
24587 'dblclick': this.onEditorEvent,
24588 'click': this.onEditorEvent,
24589 'keyup': this.onEditorEvent,
24594 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24596 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24597 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24599 this.initialized = true;
24601 this.owner.fireEvent('initialize', this);
24606 onDestroy : function(){
24612 //for (var i =0; i < this.toolbars.length;i++) {
24613 // // fixme - ask toolbars for heights?
24614 // this.toolbars[i].onDestroy();
24617 //this.wrap.dom.innerHTML = '';
24618 //this.wrap.remove();
24623 onFirstFocus : function(){
24625 this.assignDocWin();
24628 this.activated = true;
24631 if(Roo.isGecko){ // prevent silly gecko errors
24633 var s = this.win.getSelection();
24634 if(!s.focusNode || s.focusNode.nodeType != 3){
24635 var r = s.getRangeAt(0);
24636 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24641 this.execCmd('useCSS', true);
24642 this.execCmd('styleWithCSS', false);
24645 this.owner.fireEvent('activate', this);
24649 adjustFont: function(btn){
24650 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24651 //if(Roo.isSafari){ // safari
24654 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24655 if(Roo.isSafari){ // safari
24656 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24657 v = (v < 10) ? 10 : v;
24658 v = (v > 48) ? 48 : v;
24659 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24664 v = Math.max(1, v+adjust);
24666 this.execCmd('FontSize', v );
24669 onEditorEvent : function(e)
24671 this.owner.fireEvent('editorevent', this, e);
24672 // this.updateToolbar();
24673 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24676 insertTag : function(tg)
24678 // could be a bit smarter... -> wrap the current selected tRoo..
24679 if (tg.toLowerCase() == 'span' ||
24680 tg.toLowerCase() == 'code' ||
24681 tg.toLowerCase() == 'sup' ||
24682 tg.toLowerCase() == 'sub'
24685 range = this.createRange(this.getSelection());
24686 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24687 wrappingNode.appendChild(range.extractContents());
24688 range.insertNode(wrappingNode);
24695 this.execCmd("formatblock", tg);
24699 insertText : function(txt)
24703 var range = this.createRange();
24704 range.deleteContents();
24705 //alert(Sender.getAttribute('label'));
24707 range.insertNode(this.doc.createTextNode(txt));
24713 * Executes a Midas editor command on the editor document and performs necessary focus and
24714 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24715 * @param {String} cmd The Midas command
24716 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24718 relayCmd : function(cmd, value){
24720 this.execCmd(cmd, value);
24721 this.owner.fireEvent('editorevent', this);
24722 //this.updateToolbar();
24723 this.owner.deferFocus();
24727 * Executes a Midas editor command directly on the editor document.
24728 * For visual commands, you should use {@link #relayCmd} instead.
24729 * <b>This should only be called after the editor is initialized.</b>
24730 * @param {String} cmd The Midas command
24731 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24733 execCmd : function(cmd, value){
24734 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24741 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24743 * @param {String} text | dom node..
24745 insertAtCursor : function(text)
24748 if(!this.activated){
24754 var r = this.doc.selection.createRange();
24765 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24769 // from jquery ui (MIT licenced)
24771 var win = this.win;
24773 if (win.getSelection && win.getSelection().getRangeAt) {
24774 range = win.getSelection().getRangeAt(0);
24775 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24776 range.insertNode(node);
24777 } else if (win.document.selection && win.document.selection.createRange) {
24778 // no firefox support
24779 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24780 win.document.selection.createRange().pasteHTML(txt);
24782 // no firefox support
24783 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24784 this.execCmd('InsertHTML', txt);
24793 mozKeyPress : function(e){
24795 var c = e.getCharCode(), cmd;
24798 c = String.fromCharCode(c).toLowerCase();
24812 this.cleanUpPaste.defer(100, this);
24820 e.preventDefault();
24828 fixKeys : function(){ // load time branching for fastest keydown performance
24830 return function(e){
24831 var k = e.getKey(), r;
24834 r = this.doc.selection.createRange();
24837 r.pasteHTML('    ');
24844 r = this.doc.selection.createRange();
24846 var target = r.parentElement();
24847 if(!target || target.tagName.toLowerCase() != 'li'){
24849 r.pasteHTML('<br />');
24855 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24856 this.cleanUpPaste.defer(100, this);
24862 }else if(Roo.isOpera){
24863 return function(e){
24864 var k = e.getKey();
24868 this.execCmd('InsertHTML','    ');
24871 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24872 this.cleanUpPaste.defer(100, this);
24877 }else if(Roo.isSafari){
24878 return function(e){
24879 var k = e.getKey();
24883 this.execCmd('InsertText','\t');
24887 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24888 this.cleanUpPaste.defer(100, this);
24896 getAllAncestors: function()
24898 var p = this.getSelectedNode();
24901 a.push(p); // push blank onto stack..
24902 p = this.getParentElement();
24906 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24910 a.push(this.doc.body);
24914 lastSelNode : false,
24917 getSelection : function()
24919 this.assignDocWin();
24920 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24923 getSelectedNode: function()
24925 // this may only work on Gecko!!!
24927 // should we cache this!!!!
24932 var range = this.createRange(this.getSelection()).cloneRange();
24935 var parent = range.parentElement();
24937 var testRange = range.duplicate();
24938 testRange.moveToElementText(parent);
24939 if (testRange.inRange(range)) {
24942 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24945 parent = parent.parentElement;
24950 // is ancestor a text element.
24951 var ac = range.commonAncestorContainer;
24952 if (ac.nodeType == 3) {
24953 ac = ac.parentNode;
24956 var ar = ac.childNodes;
24959 var other_nodes = [];
24960 var has_other_nodes = false;
24961 for (var i=0;i<ar.length;i++) {
24962 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24965 // fullly contained node.
24967 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24972 // probably selected..
24973 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24974 other_nodes.push(ar[i]);
24978 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24983 has_other_nodes = true;
24985 if (!nodes.length && other_nodes.length) {
24986 nodes= other_nodes;
24988 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24994 createRange: function(sel)
24996 // this has strange effects when using with
24997 // top toolbar - not sure if it's a great idea.
24998 //this.editor.contentWindow.focus();
24999 if (typeof sel != "undefined") {
25001 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25003 return this.doc.createRange();
25006 return this.doc.createRange();
25009 getParentElement: function()
25012 this.assignDocWin();
25013 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25015 var range = this.createRange(sel);
25018 var p = range.commonAncestorContainer;
25019 while (p.nodeType == 3) { // text node
25030 * Range intersection.. the hard stuff...
25034 * [ -- selected range --- ]
25038 * if end is before start or hits it. fail.
25039 * if start is after end or hits it fail.
25041 * if either hits (but other is outside. - then it's not
25047 // @see http://www.thismuchiknow.co.uk/?p=64.
25048 rangeIntersectsNode : function(range, node)
25050 var nodeRange = node.ownerDocument.createRange();
25052 nodeRange.selectNode(node);
25054 nodeRange.selectNodeContents(node);
25057 var rangeStartRange = range.cloneRange();
25058 rangeStartRange.collapse(true);
25060 var rangeEndRange = range.cloneRange();
25061 rangeEndRange.collapse(false);
25063 var nodeStartRange = nodeRange.cloneRange();
25064 nodeStartRange.collapse(true);
25066 var nodeEndRange = nodeRange.cloneRange();
25067 nodeEndRange.collapse(false);
25069 return rangeStartRange.compareBoundaryPoints(
25070 Range.START_TO_START, nodeEndRange) == -1 &&
25071 rangeEndRange.compareBoundaryPoints(
25072 Range.START_TO_START, nodeStartRange) == 1;
25076 rangeCompareNode : function(range, node)
25078 var nodeRange = node.ownerDocument.createRange();
25080 nodeRange.selectNode(node);
25082 nodeRange.selectNodeContents(node);
25086 range.collapse(true);
25088 nodeRange.collapse(true);
25090 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25091 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25093 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25095 var nodeIsBefore = ss == 1;
25096 var nodeIsAfter = ee == -1;
25098 if (nodeIsBefore && nodeIsAfter) {
25101 if (!nodeIsBefore && nodeIsAfter) {
25102 return 1; //right trailed.
25105 if (nodeIsBefore && !nodeIsAfter) {
25106 return 2; // left trailed.
25112 // private? - in a new class?
25113 cleanUpPaste : function()
25115 // cleans up the whole document..
25116 Roo.log('cleanuppaste');
25118 this.cleanUpChildren(this.doc.body);
25119 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25120 if (clean != this.doc.body.innerHTML) {
25121 this.doc.body.innerHTML = clean;
25126 cleanWordChars : function(input) {// change the chars to hex code
25127 var he = Roo.HtmlEditorCore;
25129 var output = input;
25130 Roo.each(he.swapCodes, function(sw) {
25131 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25133 output = output.replace(swapper, sw[1]);
25140 cleanUpChildren : function (n)
25142 if (!n.childNodes.length) {
25145 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25146 this.cleanUpChild(n.childNodes[i]);
25153 cleanUpChild : function (node)
25156 //console.log(node);
25157 if (node.nodeName == "#text") {
25158 // clean up silly Windows -- stuff?
25161 if (node.nodeName == "#comment") {
25162 node.parentNode.removeChild(node);
25163 // clean up silly Windows -- stuff?
25166 var lcname = node.tagName.toLowerCase();
25167 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25168 // whitelist of tags..
25170 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25172 node.parentNode.removeChild(node);
25177 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25179 // spans with no attributes - just remove them..
25180 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25181 remove_keep_children = true;
25184 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25185 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25187 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25188 // remove_keep_children = true;
25191 if (remove_keep_children) {
25192 this.cleanUpChildren(node);
25193 // inserts everything just before this node...
25194 while (node.childNodes.length) {
25195 var cn = node.childNodes[0];
25196 node.removeChild(cn);
25197 node.parentNode.insertBefore(cn, node);
25199 node.parentNode.removeChild(node);
25203 if (!node.attributes || !node.attributes.length) {
25208 this.cleanUpChildren(node);
25212 function cleanAttr(n,v)
25215 if (v.match(/^\./) || v.match(/^\//)) {
25218 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25221 if (v.match(/^#/)) {
25224 if (v.match(/^\{/)) { // allow template editing.
25227 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25228 node.removeAttribute(n);
25232 var cwhite = this.cwhite;
25233 var cblack = this.cblack;
25235 function cleanStyle(n,v)
25237 if (v.match(/expression/)) { //XSS?? should we even bother..
25238 node.removeAttribute(n);
25242 var parts = v.split(/;/);
25245 Roo.each(parts, function(p) {
25246 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25250 var l = p.split(':').shift().replace(/\s+/g,'');
25251 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25253 if ( cwhite.length && cblack.indexOf(l) > -1) {
25254 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25255 //node.removeAttribute(n);
25259 // only allow 'c whitelisted system attributes'
25260 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25261 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25262 //node.removeAttribute(n);
25272 if (clean.length) {
25273 node.setAttribute(n, clean.join(';'));
25275 node.removeAttribute(n);
25281 for (var i = node.attributes.length-1; i > -1 ; i--) {
25282 var a = node.attributes[i];
25285 if (a.name.toLowerCase().substr(0,2)=='on') {
25286 node.removeAttribute(a.name);
25289 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25290 node.removeAttribute(a.name);
25293 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25294 cleanAttr(a.name,a.value); // fixme..
25297 if (a.name == 'style') {
25298 cleanStyle(a.name,a.value);
25301 /// clean up MS crap..
25302 // tecnically this should be a list of valid class'es..
25305 if (a.name == 'class') {
25306 if (a.value.match(/^Mso/)) {
25307 node.removeAttribute('class');
25310 if (a.value.match(/^body$/)) {
25311 node.removeAttribute('class');
25322 this.cleanUpChildren(node);
25328 * Clean up MS wordisms...
25330 cleanWord : function(node)
25333 this.cleanWord(this.doc.body);
25338 node.nodeName == 'SPAN' &&
25339 !node.hasAttributes() &&
25340 node.childNodes.length == 1 &&
25341 node.firstChild.nodeName == "#text"
25343 var textNode = node.firstChild;
25344 node.removeChild(textNode);
25345 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25346 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25348 node.parentNode.insertBefore(textNode, node);
25349 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25350 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25352 node.parentNode.removeChild(node);
25355 if (node.nodeName == "#text") {
25356 // clean up silly Windows -- stuff?
25359 if (node.nodeName == "#comment") {
25360 node.parentNode.removeChild(node);
25361 // clean up silly Windows -- stuff?
25365 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25366 node.parentNode.removeChild(node);
25369 //Roo.log(node.tagName);
25370 // remove - but keep children..
25371 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25372 //Roo.log('-- removed');
25373 while (node.childNodes.length) {
25374 var cn = node.childNodes[0];
25375 node.removeChild(cn);
25376 node.parentNode.insertBefore(cn, node);
25377 // move node to parent - and clean it..
25378 this.cleanWord(cn);
25380 node.parentNode.removeChild(node);
25381 /// no need to iterate chidlren = it's got none..
25382 //this.iterateChildren(node, this.cleanWord);
25386 if (node.className.length) {
25388 var cn = node.className.split(/\W+/);
25390 Roo.each(cn, function(cls) {
25391 if (cls.match(/Mso[a-zA-Z]+/)) {
25396 node.className = cna.length ? cna.join(' ') : '';
25398 node.removeAttribute("class");
25402 if (node.hasAttribute("lang")) {
25403 node.removeAttribute("lang");
25406 if (node.hasAttribute("style")) {
25408 var styles = node.getAttribute("style").split(";");
25410 Roo.each(styles, function(s) {
25411 if (!s.match(/:/)) {
25414 var kv = s.split(":");
25415 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25418 // what ever is left... we allow.
25421 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25422 if (!nstyle.length) {
25423 node.removeAttribute('style');
25426 this.iterateChildren(node, this.cleanWord);
25432 * iterateChildren of a Node, calling fn each time, using this as the scole..
25433 * @param {DomNode} node node to iterate children of.
25434 * @param {Function} fn method of this class to call on each item.
25436 iterateChildren : function(node, fn)
25438 if (!node.childNodes.length) {
25441 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25442 fn.call(this, node.childNodes[i])
25448 * cleanTableWidths.
25450 * Quite often pasting from word etc.. results in tables with column and widths.
25451 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25454 cleanTableWidths : function(node)
25459 this.cleanTableWidths(this.doc.body);
25464 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25467 Roo.log(node.tagName);
25468 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25469 this.iterateChildren(node, this.cleanTableWidths);
25472 if (node.hasAttribute('width')) {
25473 node.removeAttribute('width');
25477 if (node.hasAttribute("style")) {
25480 var styles = node.getAttribute("style").split(";");
25482 Roo.each(styles, function(s) {
25483 if (!s.match(/:/)) {
25486 var kv = s.split(":");
25487 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25490 // what ever is left... we allow.
25493 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25494 if (!nstyle.length) {
25495 node.removeAttribute('style');
25499 this.iterateChildren(node, this.cleanTableWidths);
25507 domToHTML : function(currentElement, depth, nopadtext) {
25509 depth = depth || 0;
25510 nopadtext = nopadtext || false;
25512 if (!currentElement) {
25513 return this.domToHTML(this.doc.body);
25516 //Roo.log(currentElement);
25518 var allText = false;
25519 var nodeName = currentElement.nodeName;
25520 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25522 if (nodeName == '#text') {
25524 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25529 if (nodeName != 'BODY') {
25532 // Prints the node tagName, such as <A>, <IMG>, etc
25535 for(i = 0; i < currentElement.attributes.length;i++) {
25537 var aname = currentElement.attributes.item(i).name;
25538 if (!currentElement.attributes.item(i).value.length) {
25541 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25544 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25553 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25556 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25561 // Traverse the tree
25563 var currentElementChild = currentElement.childNodes.item(i);
25564 var allText = true;
25565 var innerHTML = '';
25567 while (currentElementChild) {
25568 // Formatting code (indent the tree so it looks nice on the screen)
25569 var nopad = nopadtext;
25570 if (lastnode == 'SPAN') {
25574 if (currentElementChild.nodeName == '#text') {
25575 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25576 toadd = nopadtext ? toadd : toadd.trim();
25577 if (!nopad && toadd.length > 80) {
25578 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25580 innerHTML += toadd;
25583 currentElementChild = currentElement.childNodes.item(i);
25589 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25591 // Recursively traverse the tree structure of the child node
25592 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25593 lastnode = currentElementChild.nodeName;
25595 currentElementChild=currentElement.childNodes.item(i);
25601 // The remaining code is mostly for formatting the tree
25602 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25607 ret+= "</"+tagName+">";
25613 applyBlacklists : function()
25615 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25616 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25620 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25621 if (b.indexOf(tag) > -1) {
25624 this.white.push(tag);
25628 Roo.each(w, function(tag) {
25629 if (b.indexOf(tag) > -1) {
25632 if (this.white.indexOf(tag) > -1) {
25635 this.white.push(tag);
25640 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25641 if (w.indexOf(tag) > -1) {
25644 this.black.push(tag);
25648 Roo.each(b, function(tag) {
25649 if (w.indexOf(tag) > -1) {
25652 if (this.black.indexOf(tag) > -1) {
25655 this.black.push(tag);
25660 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25661 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25665 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25666 if (b.indexOf(tag) > -1) {
25669 this.cwhite.push(tag);
25673 Roo.each(w, function(tag) {
25674 if (b.indexOf(tag) > -1) {
25677 if (this.cwhite.indexOf(tag) > -1) {
25680 this.cwhite.push(tag);
25685 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25686 if (w.indexOf(tag) > -1) {
25689 this.cblack.push(tag);
25693 Roo.each(b, function(tag) {
25694 if (w.indexOf(tag) > -1) {
25697 if (this.cblack.indexOf(tag) > -1) {
25700 this.cblack.push(tag);
25705 setStylesheets : function(stylesheets)
25707 if(typeof(stylesheets) == 'string'){
25708 Roo.get(this.iframe.contentDocument.head).createChild({
25710 rel : 'stylesheet',
25719 Roo.each(stylesheets, function(s) {
25724 Roo.get(_this.iframe.contentDocument.head).createChild({
25726 rel : 'stylesheet',
25735 removeStylesheets : function()
25739 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25744 setStyle : function(style)
25746 Roo.get(this.iframe.contentDocument.head).createChild({
25755 // hide stuff that is not compatible
25769 * @event specialkey
25773 * @cfg {String} fieldClass @hide
25776 * @cfg {String} focusClass @hide
25779 * @cfg {String} autoCreate @hide
25782 * @cfg {String} inputType @hide
25785 * @cfg {String} invalidClass @hide
25788 * @cfg {String} invalidText @hide
25791 * @cfg {String} msgFx @hide
25794 * @cfg {String} validateOnBlur @hide
25798 Roo.HtmlEditorCore.white = [
25799 'area', 'br', 'img', 'input', 'hr', 'wbr',
25801 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25802 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25803 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25804 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25805 'table', 'ul', 'xmp',
25807 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25810 'dir', 'menu', 'ol', 'ul', 'dl',
25816 Roo.HtmlEditorCore.black = [
25817 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25819 'base', 'basefont', 'bgsound', 'blink', 'body',
25820 'frame', 'frameset', 'head', 'html', 'ilayer',
25821 'iframe', 'layer', 'link', 'meta', 'object',
25822 'script', 'style' ,'title', 'xml' // clean later..
25824 Roo.HtmlEditorCore.clean = [
25825 'script', 'style', 'title', 'xml'
25827 Roo.HtmlEditorCore.remove = [
25832 Roo.HtmlEditorCore.ablack = [
25836 Roo.HtmlEditorCore.aclean = [
25837 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25841 Roo.HtmlEditorCore.pwhite= [
25842 'http', 'https', 'mailto'
25845 // white listed style attributes.
25846 Roo.HtmlEditorCore.cwhite= [
25847 // 'text-align', /// default is to allow most things..
25853 // black listed style attributes.
25854 Roo.HtmlEditorCore.cblack= [
25855 // 'font-size' -- this can be set by the project
25859 Roo.HtmlEditorCore.swapCodes =[
25860 [ 8211, "–" ],
25861 [ 8212, "—" ],
25878 * @class Roo.bootstrap.HtmlEditor
25879 * @extends Roo.bootstrap.TextArea
25880 * Bootstrap HtmlEditor class
25883 * Create a new HtmlEditor
25884 * @param {Object} config The config object
25887 Roo.bootstrap.HtmlEditor = function(config){
25888 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25889 if (!this.toolbars) {
25890 this.toolbars = [];
25893 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25896 * @event initialize
25897 * Fires when the editor is fully initialized (including the iframe)
25898 * @param {HtmlEditor} this
25903 * Fires when the editor is first receives the focus. Any insertion must wait
25904 * until after this event.
25905 * @param {HtmlEditor} this
25909 * @event beforesync
25910 * Fires before the textarea is updated with content from the editor iframe. Return false
25911 * to cancel the sync.
25912 * @param {HtmlEditor} this
25913 * @param {String} html
25917 * @event beforepush
25918 * Fires before the iframe editor is updated with content from the textarea. Return false
25919 * to cancel the push.
25920 * @param {HtmlEditor} this
25921 * @param {String} html
25926 * Fires when the textarea is updated with content from the editor iframe.
25927 * @param {HtmlEditor} this
25928 * @param {String} html
25933 * Fires when the iframe editor is updated with content from the textarea.
25934 * @param {HtmlEditor} this
25935 * @param {String} html
25939 * @event editmodechange
25940 * Fires when the editor switches edit modes
25941 * @param {HtmlEditor} this
25942 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25944 editmodechange: true,
25946 * @event editorevent
25947 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25948 * @param {HtmlEditor} this
25952 * @event firstfocus
25953 * Fires when on first focus - needed by toolbars..
25954 * @param {HtmlEditor} this
25959 * Auto save the htmlEditor value as a file into Events
25960 * @param {HtmlEditor} this
25964 * @event savedpreview
25965 * preview the saved version of htmlEditor
25966 * @param {HtmlEditor} this
25973 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25977 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25982 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25987 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25992 * @cfg {Number} height (in pixels)
25996 * @cfg {Number} width (in pixels)
26001 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26004 stylesheets: false,
26009 // private properties
26010 validationEvent : false,
26012 initialized : false,
26015 onFocus : Roo.emptyFn,
26017 hideMode:'offsets',
26019 tbContainer : false,
26023 toolbarContainer :function() {
26024 return this.wrap.select('.x-html-editor-tb',true).first();
26028 * Protected method that will not generally be called directly. It
26029 * is called when the editor creates its toolbar. Override this method if you need to
26030 * add custom toolbar buttons.
26031 * @param {HtmlEditor} editor
26033 createToolbar : function(){
26034 Roo.log('renewing');
26035 Roo.log("create toolbars");
26037 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26038 this.toolbars[0].render(this.toolbarContainer());
26042 // if (!editor.toolbars || !editor.toolbars.length) {
26043 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26046 // for (var i =0 ; i < editor.toolbars.length;i++) {
26047 // editor.toolbars[i] = Roo.factory(
26048 // typeof(editor.toolbars[i]) == 'string' ?
26049 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26050 // Roo.bootstrap.HtmlEditor);
26051 // editor.toolbars[i].init(editor);
26057 onRender : function(ct, position)
26059 // Roo.log("Call onRender: " + this.xtype);
26061 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26063 this.wrap = this.inputEl().wrap({
26064 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26067 this.editorcore.onRender(ct, position);
26069 if (this.resizable) {
26070 this.resizeEl = new Roo.Resizable(this.wrap, {
26074 minHeight : this.height,
26075 height: this.height,
26076 handles : this.resizable,
26079 resize : function(r, w, h) {
26080 _t.onResize(w,h); // -something
26086 this.createToolbar(this);
26089 if(!this.width && this.resizable){
26090 this.setSize(this.wrap.getSize());
26092 if (this.resizeEl) {
26093 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26094 // should trigger onReize..
26100 onResize : function(w, h)
26102 Roo.log('resize: ' +w + ',' + h );
26103 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26107 if(this.inputEl() ){
26108 if(typeof w == 'number'){
26109 var aw = w - this.wrap.getFrameWidth('lr');
26110 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26113 if(typeof h == 'number'){
26114 var tbh = -11; // fixme it needs to tool bar size!
26115 for (var i =0; i < this.toolbars.length;i++) {
26116 // fixme - ask toolbars for heights?
26117 tbh += this.toolbars[i].el.getHeight();
26118 //if (this.toolbars[i].footer) {
26119 // tbh += this.toolbars[i].footer.el.getHeight();
26127 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26128 ah -= 5; // knock a few pixes off for look..
26129 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26133 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26134 this.editorcore.onResize(ew,eh);
26139 * Toggles the editor between standard and source edit mode.
26140 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26142 toggleSourceEdit : function(sourceEditMode)
26144 this.editorcore.toggleSourceEdit(sourceEditMode);
26146 if(this.editorcore.sourceEditMode){
26147 Roo.log('editor - showing textarea');
26150 // Roo.log(this.syncValue());
26152 this.inputEl().removeClass(['hide', 'x-hidden']);
26153 this.inputEl().dom.removeAttribute('tabIndex');
26154 this.inputEl().focus();
26156 Roo.log('editor - hiding textarea');
26158 // Roo.log(this.pushValue());
26161 this.inputEl().addClass(['hide', 'x-hidden']);
26162 this.inputEl().dom.setAttribute('tabIndex', -1);
26163 //this.deferFocus();
26166 if(this.resizable){
26167 this.setSize(this.wrap.getSize());
26170 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26173 // private (for BoxComponent)
26174 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26176 // private (for BoxComponent)
26177 getResizeEl : function(){
26181 // private (for BoxComponent)
26182 getPositionEl : function(){
26187 initEvents : function(){
26188 this.originalValue = this.getValue();
26192 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26195 // markInvalid : Roo.emptyFn,
26197 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26200 // clearInvalid : Roo.emptyFn,
26202 setValue : function(v){
26203 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26204 this.editorcore.pushValue();
26209 deferFocus : function(){
26210 this.focus.defer(10, this);
26214 focus : function(){
26215 this.editorcore.focus();
26221 onDestroy : function(){
26227 for (var i =0; i < this.toolbars.length;i++) {
26228 // fixme - ask toolbars for heights?
26229 this.toolbars[i].onDestroy();
26232 this.wrap.dom.innerHTML = '';
26233 this.wrap.remove();
26238 onFirstFocus : function(){
26239 //Roo.log("onFirstFocus");
26240 this.editorcore.onFirstFocus();
26241 for (var i =0; i < this.toolbars.length;i++) {
26242 this.toolbars[i].onFirstFocus();
26248 syncValue : function()
26250 this.editorcore.syncValue();
26253 pushValue : function()
26255 this.editorcore.pushValue();
26259 // hide stuff that is not compatible
26273 * @event specialkey
26277 * @cfg {String} fieldClass @hide
26280 * @cfg {String} focusClass @hide
26283 * @cfg {String} autoCreate @hide
26286 * @cfg {String} inputType @hide
26290 * @cfg {String} invalidText @hide
26293 * @cfg {String} msgFx @hide
26296 * @cfg {String} validateOnBlur @hide
26305 Roo.namespace('Roo.bootstrap.htmleditor');
26307 * @class Roo.bootstrap.HtmlEditorToolbar1
26313 new Roo.bootstrap.HtmlEditor({
26316 new Roo.bootstrap.HtmlEditorToolbar1({
26317 disable : { fonts: 1 , format: 1, ..., ... , ...],
26323 * @cfg {Object} disable List of elements to disable..
26324 * @cfg {Array} btns List of additional buttons.
26328 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26331 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26334 Roo.apply(this, config);
26336 // default disabled, based on 'good practice'..
26337 this.disable = this.disable || {};
26338 Roo.applyIf(this.disable, {
26341 specialElements : true
26343 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26345 this.editor = config.editor;
26346 this.editorcore = config.editor.editorcore;
26348 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26350 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26351 // dont call parent... till later.
26353 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26358 editorcore : false,
26363 "h1","h2","h3","h4","h5","h6",
26365 "abbr", "acronym", "address", "cite", "samp", "var",
26369 onRender : function(ct, position)
26371 // Roo.log("Call onRender: " + this.xtype);
26373 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26375 this.el.dom.style.marginBottom = '0';
26377 var editorcore = this.editorcore;
26378 var editor= this.editor;
26381 var btn = function(id,cmd , toggle, handler, html){
26383 var event = toggle ? 'toggle' : 'click';
26388 xns: Roo.bootstrap,
26392 enableToggle:toggle !== false,
26394 pressed : toggle ? false : null,
26397 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26398 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26404 // var cb_box = function...
26409 xns: Roo.bootstrap,
26414 xns: Roo.bootstrap,
26418 Roo.each(this.formats, function(f) {
26419 style.menu.items.push({
26421 xns: Roo.bootstrap,
26422 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26427 editorcore.insertTag(this.tagname);
26434 children.push(style);
26436 btn('bold',false,true);
26437 btn('italic',false,true);
26438 btn('align-left', 'justifyleft',true);
26439 btn('align-center', 'justifycenter',true);
26440 btn('align-right' , 'justifyright',true);
26441 btn('link', false, false, function(btn) {
26442 //Roo.log("create link?");
26443 var url = prompt(this.createLinkText, this.defaultLinkValue);
26444 if(url && url != 'http:/'+'/'){
26445 this.editorcore.relayCmd('createlink', url);
26448 btn('list','insertunorderedlist',true);
26449 btn('pencil', false,true, function(btn){
26451 this.toggleSourceEdit(btn.pressed);
26454 if (this.editor.btns.length > 0) {
26455 for (var i = 0; i<this.editor.btns.length; i++) {
26456 children.push(this.editor.btns[i]);
26464 xns: Roo.bootstrap,
26469 xns: Roo.bootstrap,
26474 cog.menu.items.push({
26476 xns: Roo.bootstrap,
26477 html : Clean styles,
26482 editorcore.insertTag(this.tagname);
26491 this.xtype = 'NavSimplebar';
26493 for(var i=0;i< children.length;i++) {
26495 this.buttons.add(this.addxtypeChild(children[i]));
26499 editor.on('editorevent', this.updateToolbar, this);
26501 onBtnClick : function(id)
26503 this.editorcore.relayCmd(id);
26504 this.editorcore.focus();
26508 * Protected method that will not generally be called directly. It triggers
26509 * a toolbar update by reading the markup state of the current selection in the editor.
26511 updateToolbar: function(){
26513 if(!this.editorcore.activated){
26514 this.editor.onFirstFocus(); // is this neeed?
26518 var btns = this.buttons;
26519 var doc = this.editorcore.doc;
26520 btns.get('bold').setActive(doc.queryCommandState('bold'));
26521 btns.get('italic').setActive(doc.queryCommandState('italic'));
26522 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26524 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26525 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26526 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26528 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26529 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26532 var ans = this.editorcore.getAllAncestors();
26533 if (this.formatCombo) {
26536 var store = this.formatCombo.store;
26537 this.formatCombo.setValue("");
26538 for (var i =0; i < ans.length;i++) {
26539 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26541 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26549 // hides menus... - so this cant be on a menu...
26550 Roo.bootstrap.MenuMgr.hideAll();
26552 Roo.bootstrap.MenuMgr.hideAll();
26553 //this.editorsyncValue();
26555 onFirstFocus: function() {
26556 this.buttons.each(function(item){
26560 toggleSourceEdit : function(sourceEditMode){
26563 if(sourceEditMode){
26564 Roo.log("disabling buttons");
26565 this.buttons.each( function(item){
26566 if(item.cmd != 'pencil'){
26572 Roo.log("enabling buttons");
26573 if(this.editorcore.initialized){
26574 this.buttons.each( function(item){
26580 Roo.log("calling toggole on editor");
26581 // tell the editor that it's been pressed..
26582 this.editor.toggleSourceEdit(sourceEditMode);
26596 * @class Roo.bootstrap.Markdown
26597 * @extends Roo.bootstrap.TextArea
26598 * Bootstrap Showdown editable area
26599 * @cfg {string} content
26602 * Create a new Showdown
26605 Roo.bootstrap.Markdown = function(config){
26606 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26610 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26614 initEvents : function()
26617 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26618 this.markdownEl = this.el.createChild({
26619 cls : 'roo-markdown-area'
26621 this.inputEl().addClass('d-none');
26622 if (this.getValue() == '') {
26623 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26626 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26628 this.markdownEl.on('click', this.toggleTextEdit, this);
26629 this.on('blur', this.toggleTextEdit, this);
26630 this.on('specialkey', this.resizeTextArea, this);
26633 toggleTextEdit : function()
26635 var sh = this.markdownEl.getHeight();
26636 this.inputEl().addClass('d-none');
26637 this.markdownEl.addClass('d-none');
26638 if (!this.editing) {
26640 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26641 this.inputEl().removeClass('d-none');
26642 this.inputEl().focus();
26643 this.editing = true;
26646 // show showdown...
26647 this.updateMarkdown();
26648 this.markdownEl.removeClass('d-none');
26649 this.editing = false;
26652 updateMarkdown : function()
26654 if (this.getValue() == '') {
26655 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26659 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26662 resizeTextArea: function () {
26665 Roo.log([sh, this.getValue().split("\n").length * 30]);
26666 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26668 setValue : function(val)
26670 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26671 if (!this.editing) {
26672 this.updateMarkdown();
26678 if (!this.editing) {
26679 this.toggleTextEdit();
26687 * @class Roo.bootstrap.Table.AbstractSelectionModel
26688 * @extends Roo.util.Observable
26689 * Abstract base class for grid SelectionModels. It provides the interface that should be
26690 * implemented by descendant classes. This class should not be directly instantiated.
26693 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26694 this.locked = false;
26695 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26699 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26700 /** @ignore Called by the grid automatically. Do not call directly. */
26701 init : function(grid){
26707 * Locks the selections.
26710 this.locked = true;
26714 * Unlocks the selections.
26716 unlock : function(){
26717 this.locked = false;
26721 * Returns true if the selections are locked.
26722 * @return {Boolean}
26724 isLocked : function(){
26725 return this.locked;
26729 initEvents : function ()
26735 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26736 * @class Roo.bootstrap.Table.RowSelectionModel
26737 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26738 * It supports multiple selections and keyboard selection/navigation.
26740 * @param {Object} config
26743 Roo.bootstrap.Table.RowSelectionModel = function(config){
26744 Roo.apply(this, config);
26745 this.selections = new Roo.util.MixedCollection(false, function(o){
26750 this.lastActive = false;
26754 * @event selectionchange
26755 * Fires when the selection changes
26756 * @param {SelectionModel} this
26758 "selectionchange" : true,
26760 * @event afterselectionchange
26761 * Fires after the selection changes (eg. by key press or clicking)
26762 * @param {SelectionModel} this
26764 "afterselectionchange" : true,
26766 * @event beforerowselect
26767 * Fires when a row is selected being selected, return false to cancel.
26768 * @param {SelectionModel} this
26769 * @param {Number} rowIndex The selected index
26770 * @param {Boolean} keepExisting False if other selections will be cleared
26772 "beforerowselect" : true,
26775 * Fires when a row is selected.
26776 * @param {SelectionModel} this
26777 * @param {Number} rowIndex The selected index
26778 * @param {Roo.data.Record} r The record
26780 "rowselect" : true,
26782 * @event rowdeselect
26783 * Fires when a row is deselected.
26784 * @param {SelectionModel} this
26785 * @param {Number} rowIndex The selected index
26787 "rowdeselect" : true
26789 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26790 this.locked = false;
26793 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26795 * @cfg {Boolean} singleSelect
26796 * True to allow selection of only one row at a time (defaults to false)
26798 singleSelect : false,
26801 initEvents : function()
26804 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26805 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26806 //}else{ // allow click to work like normal
26807 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26809 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26810 this.grid.on("rowclick", this.handleMouseDown, this);
26812 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26813 "up" : function(e){
26815 this.selectPrevious(e.shiftKey);
26816 }else if(this.last !== false && this.lastActive !== false){
26817 var last = this.last;
26818 this.selectRange(this.last, this.lastActive-1);
26819 this.grid.getView().focusRow(this.lastActive);
26820 if(last !== false){
26824 this.selectFirstRow();
26826 this.fireEvent("afterselectionchange", this);
26828 "down" : function(e){
26830 this.selectNext(e.shiftKey);
26831 }else if(this.last !== false && this.lastActive !== false){
26832 var last = this.last;
26833 this.selectRange(this.last, this.lastActive+1);
26834 this.grid.getView().focusRow(this.lastActive);
26835 if(last !== false){
26839 this.selectFirstRow();
26841 this.fireEvent("afterselectionchange", this);
26845 this.grid.store.on('load', function(){
26846 this.selections.clear();
26849 var view = this.grid.view;
26850 view.on("refresh", this.onRefresh, this);
26851 view.on("rowupdated", this.onRowUpdated, this);
26852 view.on("rowremoved", this.onRemove, this);
26857 onRefresh : function()
26859 var ds = this.grid.store, i, v = this.grid.view;
26860 var s = this.selections;
26861 s.each(function(r){
26862 if((i = ds.indexOfId(r.id)) != -1){
26871 onRemove : function(v, index, r){
26872 this.selections.remove(r);
26876 onRowUpdated : function(v, index, r){
26877 if(this.isSelected(r)){
26878 v.onRowSelect(index);
26884 * @param {Array} records The records to select
26885 * @param {Boolean} keepExisting (optional) True to keep existing selections
26887 selectRecords : function(records, keepExisting)
26890 this.clearSelections();
26892 var ds = this.grid.store;
26893 for(var i = 0, len = records.length; i < len; i++){
26894 this.selectRow(ds.indexOf(records[i]), true);
26899 * Gets the number of selected rows.
26902 getCount : function(){
26903 return this.selections.length;
26907 * Selects the first row in the grid.
26909 selectFirstRow : function(){
26914 * Select the last row.
26915 * @param {Boolean} keepExisting (optional) True to keep existing selections
26917 selectLastRow : function(keepExisting){
26918 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26919 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26923 * Selects the row immediately following the last selected row.
26924 * @param {Boolean} keepExisting (optional) True to keep existing selections
26926 selectNext : function(keepExisting)
26928 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26929 this.selectRow(this.last+1, keepExisting);
26930 this.grid.getView().focusRow(this.last);
26935 * Selects the row that precedes the last selected row.
26936 * @param {Boolean} keepExisting (optional) True to keep existing selections
26938 selectPrevious : function(keepExisting){
26940 this.selectRow(this.last-1, keepExisting);
26941 this.grid.getView().focusRow(this.last);
26946 * Returns the selected records
26947 * @return {Array} Array of selected records
26949 getSelections : function(){
26950 return [].concat(this.selections.items);
26954 * Returns the first selected record.
26957 getSelected : function(){
26958 return this.selections.itemAt(0);
26963 * Clears all selections.
26965 clearSelections : function(fast)
26971 var ds = this.grid.store;
26972 var s = this.selections;
26973 s.each(function(r){
26974 this.deselectRow(ds.indexOfId(r.id));
26978 this.selections.clear();
26985 * Selects all rows.
26987 selectAll : function(){
26991 this.selections.clear();
26992 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26993 this.selectRow(i, true);
26998 * Returns True if there is a selection.
26999 * @return {Boolean}
27001 hasSelection : function(){
27002 return this.selections.length > 0;
27006 * Returns True if the specified row is selected.
27007 * @param {Number/Record} record The record or index of the record to check
27008 * @return {Boolean}
27010 isSelected : function(index){
27011 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27012 return (r && this.selections.key(r.id) ? true : false);
27016 * Returns True if the specified record id is selected.
27017 * @param {String} id The id of record to check
27018 * @return {Boolean}
27020 isIdSelected : function(id){
27021 return (this.selections.key(id) ? true : false);
27026 handleMouseDBClick : function(e, t){
27030 handleMouseDown : function(e, t)
27032 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27033 if(this.isLocked() || rowIndex < 0 ){
27036 if(e.shiftKey && this.last !== false){
27037 var last = this.last;
27038 this.selectRange(last, rowIndex, e.ctrlKey);
27039 this.last = last; // reset the last
27043 var isSelected = this.isSelected(rowIndex);
27044 //Roo.log("select row:" + rowIndex);
27046 this.deselectRow(rowIndex);
27048 this.selectRow(rowIndex, true);
27052 if(e.button !== 0 && isSelected){
27053 alert('rowIndex 2: ' + rowIndex);
27054 view.focusRow(rowIndex);
27055 }else if(e.ctrlKey && isSelected){
27056 this.deselectRow(rowIndex);
27057 }else if(!isSelected){
27058 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27059 view.focusRow(rowIndex);
27063 this.fireEvent("afterselectionchange", this);
27066 handleDragableRowClick : function(grid, rowIndex, e)
27068 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27069 this.selectRow(rowIndex, false);
27070 grid.view.focusRow(rowIndex);
27071 this.fireEvent("afterselectionchange", this);
27076 * Selects multiple rows.
27077 * @param {Array} rows Array of the indexes of the row to select
27078 * @param {Boolean} keepExisting (optional) True to keep existing selections
27080 selectRows : function(rows, keepExisting){
27082 this.clearSelections();
27084 for(var i = 0, len = rows.length; i < len; i++){
27085 this.selectRow(rows[i], true);
27090 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27091 * @param {Number} startRow The index of the first row in the range
27092 * @param {Number} endRow The index of the last row in the range
27093 * @param {Boolean} keepExisting (optional) True to retain existing selections
27095 selectRange : function(startRow, endRow, keepExisting){
27100 this.clearSelections();
27102 if(startRow <= endRow){
27103 for(var i = startRow; i <= endRow; i++){
27104 this.selectRow(i, true);
27107 for(var i = startRow; i >= endRow; i--){
27108 this.selectRow(i, true);
27114 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27115 * @param {Number} startRow The index of the first row in the range
27116 * @param {Number} endRow The index of the last row in the range
27118 deselectRange : function(startRow, endRow, preventViewNotify){
27122 for(var i = startRow; i <= endRow; i++){
27123 this.deselectRow(i, preventViewNotify);
27129 * @param {Number} row The index of the row to select
27130 * @param {Boolean} keepExisting (optional) True to keep existing selections
27132 selectRow : function(index, keepExisting, preventViewNotify)
27134 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27137 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27138 if(!keepExisting || this.singleSelect){
27139 this.clearSelections();
27142 var r = this.grid.store.getAt(index);
27143 //console.log('selectRow - record id :' + r.id);
27145 this.selections.add(r);
27146 this.last = this.lastActive = index;
27147 if(!preventViewNotify){
27148 var proxy = new Roo.Element(
27149 this.grid.getRowDom(index)
27151 proxy.addClass('bg-info info');
27153 this.fireEvent("rowselect", this, index, r);
27154 this.fireEvent("selectionchange", this);
27160 * @param {Number} row The index of the row to deselect
27162 deselectRow : function(index, preventViewNotify)
27167 if(this.last == index){
27170 if(this.lastActive == index){
27171 this.lastActive = false;
27174 var r = this.grid.store.getAt(index);
27179 this.selections.remove(r);
27180 //.console.log('deselectRow - record id :' + r.id);
27181 if(!preventViewNotify){
27183 var proxy = new Roo.Element(
27184 this.grid.getRowDom(index)
27186 proxy.removeClass('bg-info info');
27188 this.fireEvent("rowdeselect", this, index);
27189 this.fireEvent("selectionchange", this);
27193 restoreLast : function(){
27195 this.last = this._last;
27200 acceptsNav : function(row, col, cm){
27201 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27205 onEditorKey : function(field, e){
27206 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27211 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27213 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27215 }else if(k == e.ENTER && !e.ctrlKey){
27219 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27221 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27223 }else if(k == e.ESC){
27227 g.startEditing(newCell[0], newCell[1]);
27233 * Ext JS Library 1.1.1
27234 * Copyright(c) 2006-2007, Ext JS, LLC.
27236 * Originally Released Under LGPL - original licence link has changed is not relivant.
27239 * <script type="text/javascript">
27243 * @class Roo.bootstrap.PagingToolbar
27244 * @extends Roo.bootstrap.NavSimplebar
27245 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27247 * Create a new PagingToolbar
27248 * @param {Object} config The config object
27249 * @param {Roo.data.Store} store
27251 Roo.bootstrap.PagingToolbar = function(config)
27253 // old args format still supported... - xtype is prefered..
27254 // created from xtype...
27256 this.ds = config.dataSource;
27258 if (config.store && !this.ds) {
27259 this.store= Roo.factory(config.store, Roo.data);
27260 this.ds = this.store;
27261 this.ds.xmodule = this.xmodule || false;
27264 this.toolbarItems = [];
27265 if (config.items) {
27266 this.toolbarItems = config.items;
27269 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27274 this.bind(this.ds);
27277 if (Roo.bootstrap.version == 4) {
27278 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27280 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27285 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27287 * @cfg {Roo.data.Store} dataSource
27288 * The underlying data store providing the paged data
27291 * @cfg {String/HTMLElement/Element} container
27292 * container The id or element that will contain the toolbar
27295 * @cfg {Boolean} displayInfo
27296 * True to display the displayMsg (defaults to false)
27299 * @cfg {Number} pageSize
27300 * The number of records to display per page (defaults to 20)
27304 * @cfg {String} displayMsg
27305 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27307 displayMsg : 'Displaying {0} - {1} of {2}',
27309 * @cfg {String} emptyMsg
27310 * The message to display when no records are found (defaults to "No data to display")
27312 emptyMsg : 'No data to display',
27314 * Customizable piece of the default paging text (defaults to "Page")
27317 beforePageText : "Page",
27319 * Customizable piece of the default paging text (defaults to "of %0")
27322 afterPageText : "of {0}",
27324 * Customizable piece of the default paging text (defaults to "First Page")
27327 firstText : "First Page",
27329 * Customizable piece of the default paging text (defaults to "Previous Page")
27332 prevText : "Previous Page",
27334 * Customizable piece of the default paging text (defaults to "Next Page")
27337 nextText : "Next Page",
27339 * Customizable piece of the default paging text (defaults to "Last Page")
27342 lastText : "Last Page",
27344 * Customizable piece of the default paging text (defaults to "Refresh")
27347 refreshText : "Refresh",
27351 onRender : function(ct, position)
27353 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27354 this.navgroup.parentId = this.id;
27355 this.navgroup.onRender(this.el, null);
27356 // add the buttons to the navgroup
27358 if(this.displayInfo){
27359 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27360 this.displayEl = this.el.select('.x-paging-info', true).first();
27361 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27362 // this.displayEl = navel.el.select('span',true).first();
27368 Roo.each(_this.buttons, function(e){ // this might need to use render????
27369 Roo.factory(e).render(_this.el);
27373 Roo.each(_this.toolbarItems, function(e) {
27374 _this.navgroup.addItem(e);
27378 this.first = this.navgroup.addItem({
27379 tooltip: this.firstText,
27380 cls: "prev btn-outline-secondary",
27381 html : ' <i class="fa fa-step-backward"></i>',
27383 preventDefault: true,
27384 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27387 this.prev = this.navgroup.addItem({
27388 tooltip: this.prevText,
27389 cls: "prev btn-outline-secondary",
27390 html : ' <i class="fa fa-backward"></i>',
27392 preventDefault: true,
27393 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27395 //this.addSeparator();
27398 var field = this.navgroup.addItem( {
27400 cls : 'x-paging-position btn-outline-secondary',
27402 html : this.beforePageText +
27403 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27404 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27407 this.field = field.el.select('input', true).first();
27408 this.field.on("keydown", this.onPagingKeydown, this);
27409 this.field.on("focus", function(){this.dom.select();});
27412 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27413 //this.field.setHeight(18);
27414 //this.addSeparator();
27415 this.next = this.navgroup.addItem({
27416 tooltip: this.nextText,
27417 cls: "next btn-outline-secondary",
27418 html : ' <i class="fa fa-forward"></i>',
27420 preventDefault: true,
27421 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27423 this.last = this.navgroup.addItem({
27424 tooltip: this.lastText,
27425 html : ' <i class="fa fa-step-forward"></i>',
27426 cls: "next btn-outline-secondary",
27428 preventDefault: true,
27429 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27431 //this.addSeparator();
27432 this.loading = this.navgroup.addItem({
27433 tooltip: this.refreshText,
27434 cls: "btn-outline-secondary",
27435 html : ' <i class="fa fa-refresh"></i>',
27436 preventDefault: true,
27437 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27443 updateInfo : function(){
27444 if(this.displayEl){
27445 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27446 var msg = count == 0 ?
27450 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27452 this.displayEl.update(msg);
27457 onLoad : function(ds, r, o)
27459 this.cursor = o.params && o.params.start ? o.params.start : 0;
27461 var d = this.getPageData(),
27466 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27467 this.field.dom.value = ap;
27468 this.first.setDisabled(ap == 1);
27469 this.prev.setDisabled(ap == 1);
27470 this.next.setDisabled(ap == ps);
27471 this.last.setDisabled(ap == ps);
27472 this.loading.enable();
27477 getPageData : function(){
27478 var total = this.ds.getTotalCount();
27481 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27482 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27487 onLoadError : function(){
27488 this.loading.enable();
27492 onPagingKeydown : function(e){
27493 var k = e.getKey();
27494 var d = this.getPageData();
27496 var v = this.field.dom.value, pageNum;
27497 if(!v || isNaN(pageNum = parseInt(v, 10))){
27498 this.field.dom.value = d.activePage;
27501 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27502 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27505 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))
27507 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27508 this.field.dom.value = pageNum;
27509 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27512 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27514 var v = this.field.dom.value, pageNum;
27515 var increment = (e.shiftKey) ? 10 : 1;
27516 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27519 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27520 this.field.dom.value = d.activePage;
27523 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27525 this.field.dom.value = parseInt(v, 10) + increment;
27526 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27527 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27534 beforeLoad : function(){
27536 this.loading.disable();
27541 onClick : function(which){
27550 ds.load({params:{start: 0, limit: this.pageSize}});
27553 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27556 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27559 var total = ds.getTotalCount();
27560 var extra = total % this.pageSize;
27561 var lastStart = extra ? (total - extra) : total-this.pageSize;
27562 ds.load({params:{start: lastStart, limit: this.pageSize}});
27565 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27571 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27572 * @param {Roo.data.Store} store The data store to unbind
27574 unbind : function(ds){
27575 ds.un("beforeload", this.beforeLoad, this);
27576 ds.un("load", this.onLoad, this);
27577 ds.un("loadexception", this.onLoadError, this);
27578 ds.un("remove", this.updateInfo, this);
27579 ds.un("add", this.updateInfo, this);
27580 this.ds = undefined;
27584 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27585 * @param {Roo.data.Store} store The data store to bind
27587 bind : function(ds){
27588 ds.on("beforeload", this.beforeLoad, this);
27589 ds.on("load", this.onLoad, this);
27590 ds.on("loadexception", this.onLoadError, this);
27591 ds.on("remove", this.updateInfo, this);
27592 ds.on("add", this.updateInfo, this);
27603 * @class Roo.bootstrap.MessageBar
27604 * @extends Roo.bootstrap.Component
27605 * Bootstrap MessageBar class
27606 * @cfg {String} html contents of the MessageBar
27607 * @cfg {String} weight (info | success | warning | danger) default info
27608 * @cfg {String} beforeClass insert the bar before the given class
27609 * @cfg {Boolean} closable (true | false) default false
27610 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27613 * Create a new Element
27614 * @param {Object} config The config object
27617 Roo.bootstrap.MessageBar = function(config){
27618 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27621 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27627 beforeClass: 'bootstrap-sticky-wrap',
27629 getAutoCreate : function(){
27633 cls: 'alert alert-dismissable alert-' + this.weight,
27638 html: this.html || ''
27644 cfg.cls += ' alert-messages-fixed';
27658 onRender : function(ct, position)
27660 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27663 var cfg = Roo.apply({}, this.getAutoCreate());
27667 cfg.cls += ' ' + this.cls;
27670 cfg.style = this.style;
27672 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27674 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27677 this.el.select('>button.close').on('click', this.hide, this);
27683 if (!this.rendered) {
27689 this.fireEvent('show', this);
27695 if (!this.rendered) {
27701 this.fireEvent('hide', this);
27704 update : function()
27706 // var e = this.el.dom.firstChild;
27708 // if(this.closable){
27709 // e = e.nextSibling;
27712 // e.data = this.html || '';
27714 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27730 * @class Roo.bootstrap.Graph
27731 * @extends Roo.bootstrap.Component
27732 * Bootstrap Graph class
27736 @cfg {String} graphtype bar | vbar | pie
27737 @cfg {number} g_x coodinator | centre x (pie)
27738 @cfg {number} g_y coodinator | centre y (pie)
27739 @cfg {number} g_r radius (pie)
27740 @cfg {number} g_height height of the chart (respected by all elements in the set)
27741 @cfg {number} g_width width of the chart (respected by all elements in the set)
27742 @cfg {Object} title The title of the chart
27745 -opts (object) options for the chart
27747 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27748 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27750 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.
27751 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27753 o stretch (boolean)
27755 -opts (object) options for the pie
27758 o startAngle (number)
27759 o endAngle (number)
27763 * Create a new Input
27764 * @param {Object} config The config object
27767 Roo.bootstrap.Graph = function(config){
27768 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27774 * The img click event for the img.
27775 * @param {Roo.EventObject} e
27781 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27792 //g_colors: this.colors,
27799 getAutoCreate : function(){
27810 onRender : function(ct,position){
27813 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27815 if (typeof(Raphael) == 'undefined') {
27816 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27820 this.raphael = Raphael(this.el.dom);
27822 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27823 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27824 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27825 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27827 r.text(160, 10, "Single Series Chart").attr(txtattr);
27828 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27829 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27830 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27832 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27833 r.barchart(330, 10, 300, 220, data1);
27834 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27835 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27838 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27839 // r.barchart(30, 30, 560, 250, xdata, {
27840 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27841 // axis : "0 0 1 1",
27842 // axisxlabels : xdata
27843 // //yvalues : cols,
27846 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27848 // this.load(null,xdata,{
27849 // axis : "0 0 1 1",
27850 // axisxlabels : xdata
27855 load : function(graphtype,xdata,opts)
27857 this.raphael.clear();
27859 graphtype = this.graphtype;
27864 var r = this.raphael,
27865 fin = function () {
27866 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27868 fout = function () {
27869 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27871 pfin = function() {
27872 this.sector.stop();
27873 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27876 this.label[0].stop();
27877 this.label[0].attr({ r: 7.5 });
27878 this.label[1].attr({ "font-weight": 800 });
27881 pfout = function() {
27882 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27885 this.label[0].animate({ r: 5 }, 500, "bounce");
27886 this.label[1].attr({ "font-weight": 400 });
27892 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27895 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27898 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27899 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27901 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27908 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27913 setTitle: function(o)
27918 initEvents: function() {
27921 this.el.on('click', this.onClick, this);
27925 onClick : function(e)
27927 Roo.log('img onclick');
27928 this.fireEvent('click', this, e);
27940 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27943 * @class Roo.bootstrap.dash.NumberBox
27944 * @extends Roo.bootstrap.Component
27945 * Bootstrap NumberBox class
27946 * @cfg {String} headline Box headline
27947 * @cfg {String} content Box content
27948 * @cfg {String} icon Box icon
27949 * @cfg {String} footer Footer text
27950 * @cfg {String} fhref Footer href
27953 * Create a new NumberBox
27954 * @param {Object} config The config object
27958 Roo.bootstrap.dash.NumberBox = function(config){
27959 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27963 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27972 getAutoCreate : function(){
27976 cls : 'small-box ',
27984 cls : 'roo-headline',
27985 html : this.headline
27989 cls : 'roo-content',
27990 html : this.content
28004 cls : 'ion ' + this.icon
28013 cls : 'small-box-footer',
28014 href : this.fhref || '#',
28018 cfg.cn.push(footer);
28025 onRender : function(ct,position){
28026 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28033 setHeadline: function (value)
28035 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28038 setFooter: function (value, href)
28040 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28043 this.el.select('a.small-box-footer',true).first().attr('href', href);
28048 setContent: function (value)
28050 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28053 initEvents: function()
28067 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28070 * @class Roo.bootstrap.dash.TabBox
28071 * @extends Roo.bootstrap.Component
28072 * Bootstrap TabBox class
28073 * @cfg {String} title Title of the TabBox
28074 * @cfg {String} icon Icon of the TabBox
28075 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28076 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28079 * Create a new TabBox
28080 * @param {Object} config The config object
28084 Roo.bootstrap.dash.TabBox = function(config){
28085 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28090 * When a pane is added
28091 * @param {Roo.bootstrap.dash.TabPane} pane
28095 * @event activatepane
28096 * When a pane is activated
28097 * @param {Roo.bootstrap.dash.TabPane} pane
28099 "activatepane" : true
28107 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28112 tabScrollable : false,
28114 getChildContainer : function()
28116 return this.el.select('.tab-content', true).first();
28119 getAutoCreate : function(){
28123 cls: 'pull-left header',
28131 cls: 'fa ' + this.icon
28137 cls: 'nav nav-tabs pull-right',
28143 if(this.tabScrollable){
28150 cls: 'nav nav-tabs pull-right',
28161 cls: 'nav-tabs-custom',
28166 cls: 'tab-content no-padding',
28174 initEvents : function()
28176 //Roo.log('add add pane handler');
28177 this.on('addpane', this.onAddPane, this);
28180 * Updates the box title
28181 * @param {String} html to set the title to.
28183 setTitle : function(value)
28185 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28187 onAddPane : function(pane)
28189 this.panes.push(pane);
28190 //Roo.log('addpane');
28192 // tabs are rendere left to right..
28193 if(!this.showtabs){
28197 var ctr = this.el.select('.nav-tabs', true).first();
28200 var existing = ctr.select('.nav-tab',true);
28201 var qty = existing.getCount();;
28204 var tab = ctr.createChild({
28206 cls : 'nav-tab' + (qty ? '' : ' active'),
28214 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28217 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28219 pane.el.addClass('active');
28224 onTabClick : function(ev,un,ob,pane)
28226 //Roo.log('tab - prev default');
28227 ev.preventDefault();
28230 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28231 pane.tab.addClass('active');
28232 //Roo.log(pane.title);
28233 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28234 // technically we should have a deactivate event.. but maybe add later.
28235 // and it should not de-activate the selected tab...
28236 this.fireEvent('activatepane', pane);
28237 pane.el.addClass('active');
28238 pane.fireEvent('activate');
28243 getActivePane : function()
28246 Roo.each(this.panes, function(p) {
28247 if(p.el.hasClass('active')){
28268 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28270 * @class Roo.bootstrap.TabPane
28271 * @extends Roo.bootstrap.Component
28272 * Bootstrap TabPane class
28273 * @cfg {Boolean} active (false | true) Default false
28274 * @cfg {String} title title of panel
28278 * Create a new TabPane
28279 * @param {Object} config The config object
28282 Roo.bootstrap.dash.TabPane = function(config){
28283 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28289 * When a pane is activated
28290 * @param {Roo.bootstrap.dash.TabPane} pane
28297 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28302 // the tabBox that this is attached to.
28305 getAutoCreate : function()
28313 cfg.cls += ' active';
28318 initEvents : function()
28320 //Roo.log('trigger add pane handler');
28321 this.parent().fireEvent('addpane', this)
28325 * Updates the tab title
28326 * @param {String} html to set the title to.
28328 setTitle: function(str)
28334 this.tab.select('a', true).first().dom.innerHTML = str;
28351 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28354 * @class Roo.bootstrap.menu.Menu
28355 * @extends Roo.bootstrap.Component
28356 * Bootstrap Menu class - container for Menu
28357 * @cfg {String} html Text of the menu
28358 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28359 * @cfg {String} icon Font awesome icon
28360 * @cfg {String} pos Menu align to (top | bottom) default bottom
28364 * Create a new Menu
28365 * @param {Object} config The config object
28369 Roo.bootstrap.menu.Menu = function(config){
28370 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28374 * @event beforeshow
28375 * Fires before this menu is displayed
28376 * @param {Roo.bootstrap.menu.Menu} this
28380 * @event beforehide
28381 * Fires before this menu is hidden
28382 * @param {Roo.bootstrap.menu.Menu} this
28387 * Fires after this menu is displayed
28388 * @param {Roo.bootstrap.menu.Menu} this
28393 * Fires after this menu is hidden
28394 * @param {Roo.bootstrap.menu.Menu} this
28399 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28400 * @param {Roo.bootstrap.menu.Menu} this
28401 * @param {Roo.EventObject} e
28408 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28412 weight : 'default',
28417 getChildContainer : function() {
28418 if(this.isSubMenu){
28422 return this.el.select('ul.dropdown-menu', true).first();
28425 getAutoCreate : function()
28430 cls : 'roo-menu-text',
28438 cls : 'fa ' + this.icon
28449 cls : 'dropdown-button btn btn-' + this.weight,
28454 cls : 'dropdown-toggle btn btn-' + this.weight,
28464 cls : 'dropdown-menu'
28470 if(this.pos == 'top'){
28471 cfg.cls += ' dropup';
28474 if(this.isSubMenu){
28477 cls : 'dropdown-menu'
28484 onRender : function(ct, position)
28486 this.isSubMenu = ct.hasClass('dropdown-submenu');
28488 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28491 initEvents : function()
28493 if(this.isSubMenu){
28497 this.hidden = true;
28499 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28500 this.triggerEl.on('click', this.onTriggerPress, this);
28502 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28503 this.buttonEl.on('click', this.onClick, this);
28509 if(this.isSubMenu){
28513 return this.el.select('ul.dropdown-menu', true).first();
28516 onClick : function(e)
28518 this.fireEvent("click", this, e);
28521 onTriggerPress : function(e)
28523 if (this.isVisible()) {
28530 isVisible : function(){
28531 return !this.hidden;
28536 this.fireEvent("beforeshow", this);
28538 this.hidden = false;
28539 this.el.addClass('open');
28541 Roo.get(document).on("mouseup", this.onMouseUp, this);
28543 this.fireEvent("show", this);
28550 this.fireEvent("beforehide", this);
28552 this.hidden = true;
28553 this.el.removeClass('open');
28555 Roo.get(document).un("mouseup", this.onMouseUp);
28557 this.fireEvent("hide", this);
28560 onMouseUp : function()
28574 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28577 * @class Roo.bootstrap.menu.Item
28578 * @extends Roo.bootstrap.Component
28579 * Bootstrap MenuItem class
28580 * @cfg {Boolean} submenu (true | false) default false
28581 * @cfg {String} html text of the item
28582 * @cfg {String} href the link
28583 * @cfg {Boolean} disable (true | false) default false
28584 * @cfg {Boolean} preventDefault (true | false) default true
28585 * @cfg {String} icon Font awesome icon
28586 * @cfg {String} pos Submenu align to (left | right) default right
28590 * Create a new Item
28591 * @param {Object} config The config object
28595 Roo.bootstrap.menu.Item = function(config){
28596 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28600 * Fires when the mouse is hovering over this menu
28601 * @param {Roo.bootstrap.menu.Item} this
28602 * @param {Roo.EventObject} e
28607 * Fires when the mouse exits this menu
28608 * @param {Roo.bootstrap.menu.Item} this
28609 * @param {Roo.EventObject} e
28615 * The raw click event for the entire grid.
28616 * @param {Roo.EventObject} e
28622 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28627 preventDefault: true,
28632 getAutoCreate : function()
28637 cls : 'roo-menu-item-text',
28645 cls : 'fa ' + this.icon
28654 href : this.href || '#',
28661 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28665 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28667 if(this.pos == 'left'){
28668 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28675 initEvents : function()
28677 this.el.on('mouseover', this.onMouseOver, this);
28678 this.el.on('mouseout', this.onMouseOut, this);
28680 this.el.select('a', true).first().on('click', this.onClick, this);
28684 onClick : function(e)
28686 if(this.preventDefault){
28687 e.preventDefault();
28690 this.fireEvent("click", this, e);
28693 onMouseOver : function(e)
28695 if(this.submenu && this.pos == 'left'){
28696 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28699 this.fireEvent("mouseover", this, e);
28702 onMouseOut : function(e)
28704 this.fireEvent("mouseout", this, e);
28716 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28719 * @class Roo.bootstrap.menu.Separator
28720 * @extends Roo.bootstrap.Component
28721 * Bootstrap Separator class
28724 * Create a new Separator
28725 * @param {Object} config The config object
28729 Roo.bootstrap.menu.Separator = function(config){
28730 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28733 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28735 getAutoCreate : function(){
28756 * @class Roo.bootstrap.Tooltip
28757 * Bootstrap Tooltip class
28758 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28759 * to determine which dom element triggers the tooltip.
28761 * It needs to add support for additional attributes like tooltip-position
28764 * Create a new Toolti
28765 * @param {Object} config The config object
28768 Roo.bootstrap.Tooltip = function(config){
28769 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28771 this.alignment = Roo.bootstrap.Tooltip.alignment;
28773 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28774 this.alignment = config.alignment;
28779 Roo.apply(Roo.bootstrap.Tooltip, {
28781 * @function init initialize tooltip monitoring.
28785 currentTip : false,
28786 currentRegion : false,
28792 Roo.get(document).on('mouseover', this.enter ,this);
28793 Roo.get(document).on('mouseout', this.leave, this);
28796 this.currentTip = new Roo.bootstrap.Tooltip();
28799 enter : function(ev)
28801 var dom = ev.getTarget();
28803 //Roo.log(['enter',dom]);
28804 var el = Roo.fly(dom);
28805 if (this.currentEl) {
28807 //Roo.log(this.currentEl);
28808 //Roo.log(this.currentEl.contains(dom));
28809 if (this.currentEl == el) {
28812 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28818 if (this.currentTip.el) {
28819 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28823 if(!el || el.dom == document){
28829 // you can not look for children, as if el is the body.. then everythign is the child..
28830 if (!el.attr('tooltip')) { //
28831 if (!el.select("[tooltip]").elements.length) {
28834 // is the mouse over this child...?
28835 bindEl = el.select("[tooltip]").first();
28836 var xy = ev.getXY();
28837 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28838 //Roo.log("not in region.");
28841 //Roo.log("child element over..");
28844 this.currentEl = bindEl;
28845 this.currentTip.bind(bindEl);
28846 this.currentRegion = Roo.lib.Region.getRegion(dom);
28847 this.currentTip.enter();
28850 leave : function(ev)
28852 var dom = ev.getTarget();
28853 //Roo.log(['leave',dom]);
28854 if (!this.currentEl) {
28859 if (dom != this.currentEl.dom) {
28862 var xy = ev.getXY();
28863 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28866 // only activate leave if mouse cursor is outside... bounding box..
28871 if (this.currentTip) {
28872 this.currentTip.leave();
28874 //Roo.log('clear currentEl');
28875 this.currentEl = false;
28880 'left' : ['r-l', [-2,0], 'right'],
28881 'right' : ['l-r', [2,0], 'left'],
28882 'bottom' : ['t-b', [0,2], 'top'],
28883 'top' : [ 'b-t', [0,-2], 'bottom']
28889 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28894 delay : null, // can be { show : 300 , hide: 500}
28898 hoverState : null, //???
28900 placement : 'bottom',
28904 getAutoCreate : function(){
28911 cls : 'tooltip-arrow arrow'
28914 cls : 'tooltip-inner'
28921 bind : function(el)
28926 initEvents : function()
28928 this.arrowEl = this.el.select('.arrow', true).first();
28929 this.innerEl = this.el.select('.tooltip-inner', true).first();
28932 enter : function () {
28934 if (this.timeout != null) {
28935 clearTimeout(this.timeout);
28938 this.hoverState = 'in';
28939 //Roo.log("enter - show");
28940 if (!this.delay || !this.delay.show) {
28945 this.timeout = setTimeout(function () {
28946 if (_t.hoverState == 'in') {
28949 }, this.delay.show);
28953 clearTimeout(this.timeout);
28955 this.hoverState = 'out';
28956 if (!this.delay || !this.delay.hide) {
28962 this.timeout = setTimeout(function () {
28963 //Roo.log("leave - timeout");
28965 if (_t.hoverState == 'out') {
28967 Roo.bootstrap.Tooltip.currentEl = false;
28972 show : function (msg)
28975 this.render(document.body);
28978 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28980 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28982 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28984 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28985 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28987 var placement = typeof this.placement == 'function' ?
28988 this.placement.call(this, this.el, on_el) :
28991 var autoToken = /\s?auto?\s?/i;
28992 var autoPlace = autoToken.test(placement);
28994 placement = placement.replace(autoToken, '') || 'top';
28998 //this.el.setXY([0,0]);
29000 //this.el.dom.style.display='block';
29002 //this.el.appendTo(on_el);
29004 var p = this.getPosition();
29005 var box = this.el.getBox();
29011 var align = this.alignment[placement];
29013 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29015 if(placement == 'top' || placement == 'bottom'){
29017 placement = 'right';
29020 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29021 placement = 'left';
29024 var scroll = Roo.select('body', true).first().getScroll();
29026 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29030 align = this.alignment[placement];
29032 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29036 this.el.alignTo(this.bindEl, align[0],align[1]);
29037 //var arrow = this.el.select('.arrow',true).first();
29038 //arrow.set(align[2],
29040 this.el.addClass(placement);
29041 this.el.addClass("bs-tooltip-"+ placement);
29043 this.el.addClass('in fade show');
29045 this.hoverState = null;
29047 if (this.el.hasClass('fade')) {
29062 //this.el.setXY([0,0]);
29063 this.el.removeClass(['show', 'in']);
29079 * @class Roo.bootstrap.LocationPicker
29080 * @extends Roo.bootstrap.Component
29081 * Bootstrap LocationPicker class
29082 * @cfg {Number} latitude Position when init default 0
29083 * @cfg {Number} longitude Position when init default 0
29084 * @cfg {Number} zoom default 15
29085 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29086 * @cfg {Boolean} mapTypeControl default false
29087 * @cfg {Boolean} disableDoubleClickZoom default false
29088 * @cfg {Boolean} scrollwheel default true
29089 * @cfg {Boolean} streetViewControl default false
29090 * @cfg {Number} radius default 0
29091 * @cfg {String} locationName
29092 * @cfg {Boolean} draggable default true
29093 * @cfg {Boolean} enableAutocomplete default false
29094 * @cfg {Boolean} enableReverseGeocode default true
29095 * @cfg {String} markerTitle
29098 * Create a new LocationPicker
29099 * @param {Object} config The config object
29103 Roo.bootstrap.LocationPicker = function(config){
29105 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29110 * Fires when the picker initialized.
29111 * @param {Roo.bootstrap.LocationPicker} this
29112 * @param {Google Location} location
29116 * @event positionchanged
29117 * Fires when the picker position changed.
29118 * @param {Roo.bootstrap.LocationPicker} this
29119 * @param {Google Location} location
29121 positionchanged : true,
29124 * Fires when the map resize.
29125 * @param {Roo.bootstrap.LocationPicker} this
29130 * Fires when the map show.
29131 * @param {Roo.bootstrap.LocationPicker} this
29136 * Fires when the map hide.
29137 * @param {Roo.bootstrap.LocationPicker} this
29142 * Fires when click the map.
29143 * @param {Roo.bootstrap.LocationPicker} this
29144 * @param {Map event} e
29148 * @event mapRightClick
29149 * Fires when right click the map.
29150 * @param {Roo.bootstrap.LocationPicker} this
29151 * @param {Map event} e
29153 mapRightClick : true,
29155 * @event markerClick
29156 * Fires when click the marker.
29157 * @param {Roo.bootstrap.LocationPicker} this
29158 * @param {Map event} e
29160 markerClick : true,
29162 * @event markerRightClick
29163 * Fires when right click the marker.
29164 * @param {Roo.bootstrap.LocationPicker} this
29165 * @param {Map event} e
29167 markerRightClick : true,
29169 * @event OverlayViewDraw
29170 * Fires when OverlayView Draw
29171 * @param {Roo.bootstrap.LocationPicker} this
29173 OverlayViewDraw : true,
29175 * @event OverlayViewOnAdd
29176 * Fires when OverlayView Draw
29177 * @param {Roo.bootstrap.LocationPicker} this
29179 OverlayViewOnAdd : true,
29181 * @event OverlayViewOnRemove
29182 * Fires when OverlayView Draw
29183 * @param {Roo.bootstrap.LocationPicker} this
29185 OverlayViewOnRemove : true,
29187 * @event OverlayViewShow
29188 * Fires when OverlayView Draw
29189 * @param {Roo.bootstrap.LocationPicker} this
29190 * @param {Pixel} cpx
29192 OverlayViewShow : true,
29194 * @event OverlayViewHide
29195 * Fires when OverlayView Draw
29196 * @param {Roo.bootstrap.LocationPicker} this
29198 OverlayViewHide : true,
29200 * @event loadexception
29201 * Fires when load google lib failed.
29202 * @param {Roo.bootstrap.LocationPicker} this
29204 loadexception : true
29209 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29211 gMapContext: false,
29217 mapTypeControl: false,
29218 disableDoubleClickZoom: false,
29220 streetViewControl: false,
29224 enableAutocomplete: false,
29225 enableReverseGeocode: true,
29228 getAutoCreate: function()
29233 cls: 'roo-location-picker'
29239 initEvents: function(ct, position)
29241 if(!this.el.getWidth() || this.isApplied()){
29245 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29250 initial: function()
29252 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29253 this.fireEvent('loadexception', this);
29257 if(!this.mapTypeId){
29258 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29261 this.gMapContext = this.GMapContext();
29263 this.initOverlayView();
29265 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29269 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29270 _this.setPosition(_this.gMapContext.marker.position);
29273 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29274 _this.fireEvent('mapClick', this, event);
29278 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29279 _this.fireEvent('mapRightClick', this, event);
29283 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29284 _this.fireEvent('markerClick', this, event);
29288 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29289 _this.fireEvent('markerRightClick', this, event);
29293 this.setPosition(this.gMapContext.location);
29295 this.fireEvent('initial', this, this.gMapContext.location);
29298 initOverlayView: function()
29302 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29306 _this.fireEvent('OverlayViewDraw', _this);
29311 _this.fireEvent('OverlayViewOnAdd', _this);
29314 onRemove: function()
29316 _this.fireEvent('OverlayViewOnRemove', _this);
29319 show: function(cpx)
29321 _this.fireEvent('OverlayViewShow', _this, cpx);
29326 _this.fireEvent('OverlayViewHide', _this);
29332 fromLatLngToContainerPixel: function(event)
29334 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29337 isApplied: function()
29339 return this.getGmapContext() == false ? false : true;
29342 getGmapContext: function()
29344 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29347 GMapContext: function()
29349 var position = new google.maps.LatLng(this.latitude, this.longitude);
29351 var _map = new google.maps.Map(this.el.dom, {
29354 mapTypeId: this.mapTypeId,
29355 mapTypeControl: this.mapTypeControl,
29356 disableDoubleClickZoom: this.disableDoubleClickZoom,
29357 scrollwheel: this.scrollwheel,
29358 streetViewControl: this.streetViewControl,
29359 locationName: this.locationName,
29360 draggable: this.draggable,
29361 enableAutocomplete: this.enableAutocomplete,
29362 enableReverseGeocode: this.enableReverseGeocode
29365 var _marker = new google.maps.Marker({
29366 position: position,
29368 title: this.markerTitle,
29369 draggable: this.draggable
29376 location: position,
29377 radius: this.radius,
29378 locationName: this.locationName,
29379 addressComponents: {
29380 formatted_address: null,
29381 addressLine1: null,
29382 addressLine2: null,
29384 streetNumber: null,
29388 stateOrProvince: null
29391 domContainer: this.el.dom,
29392 geodecoder: new google.maps.Geocoder()
29396 drawCircle: function(center, radius, options)
29398 if (this.gMapContext.circle != null) {
29399 this.gMapContext.circle.setMap(null);
29403 options = Roo.apply({}, options, {
29404 strokeColor: "#0000FF",
29405 strokeOpacity: .35,
29407 fillColor: "#0000FF",
29411 options.map = this.gMapContext.map;
29412 options.radius = radius;
29413 options.center = center;
29414 this.gMapContext.circle = new google.maps.Circle(options);
29415 return this.gMapContext.circle;
29421 setPosition: function(location)
29423 this.gMapContext.location = location;
29424 this.gMapContext.marker.setPosition(location);
29425 this.gMapContext.map.panTo(location);
29426 this.drawCircle(location, this.gMapContext.radius, {});
29430 if (this.gMapContext.settings.enableReverseGeocode) {
29431 this.gMapContext.geodecoder.geocode({
29432 latLng: this.gMapContext.location
29433 }, function(results, status) {
29435 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29436 _this.gMapContext.locationName = results[0].formatted_address;
29437 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29439 _this.fireEvent('positionchanged', this, location);
29446 this.fireEvent('positionchanged', this, location);
29451 google.maps.event.trigger(this.gMapContext.map, "resize");
29453 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29455 this.fireEvent('resize', this);
29458 setPositionByLatLng: function(latitude, longitude)
29460 this.setPosition(new google.maps.LatLng(latitude, longitude));
29463 getCurrentPosition: function()
29466 latitude: this.gMapContext.location.lat(),
29467 longitude: this.gMapContext.location.lng()
29471 getAddressName: function()
29473 return this.gMapContext.locationName;
29476 getAddressComponents: function()
29478 return this.gMapContext.addressComponents;
29481 address_component_from_google_geocode: function(address_components)
29485 for (var i = 0; i < address_components.length; i++) {
29486 var component = address_components[i];
29487 if (component.types.indexOf("postal_code") >= 0) {
29488 result.postalCode = component.short_name;
29489 } else if (component.types.indexOf("street_number") >= 0) {
29490 result.streetNumber = component.short_name;
29491 } else if (component.types.indexOf("route") >= 0) {
29492 result.streetName = component.short_name;
29493 } else if (component.types.indexOf("neighborhood") >= 0) {
29494 result.city = component.short_name;
29495 } else if (component.types.indexOf("locality") >= 0) {
29496 result.city = component.short_name;
29497 } else if (component.types.indexOf("sublocality") >= 0) {
29498 result.district = component.short_name;
29499 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29500 result.stateOrProvince = component.short_name;
29501 } else if (component.types.indexOf("country") >= 0) {
29502 result.country = component.short_name;
29506 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29507 result.addressLine2 = "";
29511 setZoomLevel: function(zoom)
29513 this.gMapContext.map.setZoom(zoom);
29526 this.fireEvent('show', this);
29537 this.fireEvent('hide', this);
29542 Roo.apply(Roo.bootstrap.LocationPicker, {
29544 OverlayView : function(map, options)
29546 options = options || {};
29553 * @class Roo.bootstrap.Alert
29554 * @extends Roo.bootstrap.Component
29555 * Bootstrap Alert class - shows an alert area box
29557 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29558 Enter a valid email address
29561 * @cfg {String} title The title of alert
29562 * @cfg {String} html The content of alert
29563 * @cfg {String} weight ( success | info | warning | danger )
29564 * @cfg {String} faicon font-awesomeicon
29567 * Create a new alert
29568 * @param {Object} config The config object
29572 Roo.bootstrap.Alert = function(config){
29573 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29577 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29584 getAutoCreate : function()
29593 cls : 'roo-alert-icon'
29598 cls : 'roo-alert-title',
29603 cls : 'roo-alert-text',
29610 cfg.cn[0].cls += ' fa ' + this.faicon;
29614 cfg.cls += ' alert-' + this.weight;
29620 initEvents: function()
29622 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29625 setTitle : function(str)
29627 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29630 setText : function(str)
29632 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29635 setWeight : function(weight)
29638 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29641 this.weight = weight;
29643 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29646 setIcon : function(icon)
29649 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29652 this.faicon = icon;
29654 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29675 * @class Roo.bootstrap.UploadCropbox
29676 * @extends Roo.bootstrap.Component
29677 * Bootstrap UploadCropbox class
29678 * @cfg {String} emptyText show when image has been loaded
29679 * @cfg {String} rotateNotify show when image too small to rotate
29680 * @cfg {Number} errorTimeout default 3000
29681 * @cfg {Number} minWidth default 300
29682 * @cfg {Number} minHeight default 300
29683 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29684 * @cfg {Boolean} isDocument (true|false) default false
29685 * @cfg {String} url action url
29686 * @cfg {String} paramName default 'imageUpload'
29687 * @cfg {String} method default POST
29688 * @cfg {Boolean} loadMask (true|false) default true
29689 * @cfg {Boolean} loadingText default 'Loading...'
29692 * Create a new UploadCropbox
29693 * @param {Object} config The config object
29696 Roo.bootstrap.UploadCropbox = function(config){
29697 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29701 * @event beforeselectfile
29702 * Fire before select file
29703 * @param {Roo.bootstrap.UploadCropbox} this
29705 "beforeselectfile" : true,
29708 * Fire after initEvent
29709 * @param {Roo.bootstrap.UploadCropbox} this
29714 * Fire after initEvent
29715 * @param {Roo.bootstrap.UploadCropbox} this
29716 * @param {String} data
29721 * Fire when preparing the file data
29722 * @param {Roo.bootstrap.UploadCropbox} this
29723 * @param {Object} file
29728 * Fire when get exception
29729 * @param {Roo.bootstrap.UploadCropbox} this
29730 * @param {XMLHttpRequest} xhr
29732 "exception" : true,
29734 * @event beforeloadcanvas
29735 * Fire before load the canvas
29736 * @param {Roo.bootstrap.UploadCropbox} this
29737 * @param {String} src
29739 "beforeloadcanvas" : true,
29742 * Fire when trash image
29743 * @param {Roo.bootstrap.UploadCropbox} this
29748 * Fire when download the image
29749 * @param {Roo.bootstrap.UploadCropbox} this
29753 * @event footerbuttonclick
29754 * Fire when footerbuttonclick
29755 * @param {Roo.bootstrap.UploadCropbox} this
29756 * @param {String} type
29758 "footerbuttonclick" : true,
29762 * @param {Roo.bootstrap.UploadCropbox} this
29767 * Fire when rotate the image
29768 * @param {Roo.bootstrap.UploadCropbox} this
29769 * @param {String} pos
29774 * Fire when inspect the file
29775 * @param {Roo.bootstrap.UploadCropbox} this
29776 * @param {Object} file
29781 * Fire when xhr upload the file
29782 * @param {Roo.bootstrap.UploadCropbox} this
29783 * @param {Object} data
29788 * Fire when arrange the file data
29789 * @param {Roo.bootstrap.UploadCropbox} this
29790 * @param {Object} formData
29795 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29798 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29800 emptyText : 'Click to upload image',
29801 rotateNotify : 'Image is too small to rotate',
29802 errorTimeout : 3000,
29816 cropType : 'image/jpeg',
29818 canvasLoaded : false,
29819 isDocument : false,
29821 paramName : 'imageUpload',
29823 loadingText : 'Loading...',
29826 getAutoCreate : function()
29830 cls : 'roo-upload-cropbox',
29834 cls : 'roo-upload-cropbox-selector',
29839 cls : 'roo-upload-cropbox-body',
29840 style : 'cursor:pointer',
29844 cls : 'roo-upload-cropbox-preview'
29848 cls : 'roo-upload-cropbox-thumb'
29852 cls : 'roo-upload-cropbox-empty-notify',
29853 html : this.emptyText
29857 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29858 html : this.rotateNotify
29864 cls : 'roo-upload-cropbox-footer',
29867 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29877 onRender : function(ct, position)
29879 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29881 if (this.buttons.length) {
29883 Roo.each(this.buttons, function(bb) {
29885 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29887 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29893 this.maskEl = this.el;
29897 initEvents : function()
29899 this.urlAPI = (window.createObjectURL && window) ||
29900 (window.URL && URL.revokeObjectURL && URL) ||
29901 (window.webkitURL && webkitURL);
29903 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29904 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29906 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29907 this.selectorEl.hide();
29909 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29910 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29912 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29913 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29914 this.thumbEl.hide();
29916 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29917 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29919 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29920 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29921 this.errorEl.hide();
29923 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29924 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29925 this.footerEl.hide();
29927 this.setThumbBoxSize();
29933 this.fireEvent('initial', this);
29940 window.addEventListener("resize", function() { _this.resize(); } );
29942 this.bodyEl.on('click', this.beforeSelectFile, this);
29945 this.bodyEl.on('touchstart', this.onTouchStart, this);
29946 this.bodyEl.on('touchmove', this.onTouchMove, this);
29947 this.bodyEl.on('touchend', this.onTouchEnd, this);
29951 this.bodyEl.on('mousedown', this.onMouseDown, this);
29952 this.bodyEl.on('mousemove', this.onMouseMove, this);
29953 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29954 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29955 Roo.get(document).on('mouseup', this.onMouseUp, this);
29958 this.selectorEl.on('change', this.onFileSelected, this);
29964 this.baseScale = 1;
29966 this.baseRotate = 1;
29967 this.dragable = false;
29968 this.pinching = false;
29971 this.cropData = false;
29972 this.notifyEl.dom.innerHTML = this.emptyText;
29974 this.selectorEl.dom.value = '';
29978 resize : function()
29980 if(this.fireEvent('resize', this) != false){
29981 this.setThumbBoxPosition();
29982 this.setCanvasPosition();
29986 onFooterButtonClick : function(e, el, o, type)
29989 case 'rotate-left' :
29990 this.onRotateLeft(e);
29992 case 'rotate-right' :
29993 this.onRotateRight(e);
29996 this.beforeSelectFile(e);
30011 this.fireEvent('footerbuttonclick', this, type);
30014 beforeSelectFile : function(e)
30016 e.preventDefault();
30018 if(this.fireEvent('beforeselectfile', this) != false){
30019 this.selectorEl.dom.click();
30023 onFileSelected : function(e)
30025 e.preventDefault();
30027 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30031 var file = this.selectorEl.dom.files[0];
30033 if(this.fireEvent('inspect', this, file) != false){
30034 this.prepare(file);
30039 trash : function(e)
30041 this.fireEvent('trash', this);
30044 download : function(e)
30046 this.fireEvent('download', this);
30049 loadCanvas : function(src)
30051 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30055 this.imageEl = document.createElement('img');
30059 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30061 this.imageEl.src = src;
30065 onLoadCanvas : function()
30067 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30068 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30070 this.bodyEl.un('click', this.beforeSelectFile, this);
30072 this.notifyEl.hide();
30073 this.thumbEl.show();
30074 this.footerEl.show();
30076 this.baseRotateLevel();
30078 if(this.isDocument){
30079 this.setThumbBoxSize();
30082 this.setThumbBoxPosition();
30084 this.baseScaleLevel();
30090 this.canvasLoaded = true;
30093 this.maskEl.unmask();
30098 setCanvasPosition : function()
30100 if(!this.canvasEl){
30104 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30105 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30107 this.previewEl.setLeft(pw);
30108 this.previewEl.setTop(ph);
30112 onMouseDown : function(e)
30116 this.dragable = true;
30117 this.pinching = false;
30119 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30120 this.dragable = false;
30124 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30125 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30129 onMouseMove : function(e)
30133 if(!this.canvasLoaded){
30137 if (!this.dragable){
30141 var minX = Math.ceil(this.thumbEl.getLeft(true));
30142 var minY = Math.ceil(this.thumbEl.getTop(true));
30144 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30145 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30147 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30148 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30150 x = x - this.mouseX;
30151 y = y - this.mouseY;
30153 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30154 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30156 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30157 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30159 this.previewEl.setLeft(bgX);
30160 this.previewEl.setTop(bgY);
30162 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30163 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30166 onMouseUp : function(e)
30170 this.dragable = false;
30173 onMouseWheel : function(e)
30177 this.startScale = this.scale;
30179 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30181 if(!this.zoomable()){
30182 this.scale = this.startScale;
30191 zoomable : function()
30193 var minScale = this.thumbEl.getWidth() / this.minWidth;
30195 if(this.minWidth < this.minHeight){
30196 minScale = this.thumbEl.getHeight() / this.minHeight;
30199 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30200 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30204 (this.rotate == 0 || this.rotate == 180) &&
30206 width > this.imageEl.OriginWidth ||
30207 height > this.imageEl.OriginHeight ||
30208 (width < this.minWidth && height < this.minHeight)
30216 (this.rotate == 90 || this.rotate == 270) &&
30218 width > this.imageEl.OriginWidth ||
30219 height > this.imageEl.OriginHeight ||
30220 (width < this.minHeight && height < this.minWidth)
30227 !this.isDocument &&
30228 (this.rotate == 0 || this.rotate == 180) &&
30230 width < this.minWidth ||
30231 width > this.imageEl.OriginWidth ||
30232 height < this.minHeight ||
30233 height > this.imageEl.OriginHeight
30240 !this.isDocument &&
30241 (this.rotate == 90 || this.rotate == 270) &&
30243 width < this.minHeight ||
30244 width > this.imageEl.OriginWidth ||
30245 height < this.minWidth ||
30246 height > this.imageEl.OriginHeight
30256 onRotateLeft : function(e)
30258 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30260 var minScale = this.thumbEl.getWidth() / this.minWidth;
30262 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30263 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30265 this.startScale = this.scale;
30267 while (this.getScaleLevel() < minScale){
30269 this.scale = this.scale + 1;
30271 if(!this.zoomable()){
30276 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30277 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30282 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30289 this.scale = this.startScale;
30291 this.onRotateFail();
30296 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30298 if(this.isDocument){
30299 this.setThumbBoxSize();
30300 this.setThumbBoxPosition();
30301 this.setCanvasPosition();
30306 this.fireEvent('rotate', this, 'left');
30310 onRotateRight : function(e)
30312 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30314 var minScale = this.thumbEl.getWidth() / this.minWidth;
30316 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30317 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30319 this.startScale = this.scale;
30321 while (this.getScaleLevel() < minScale){
30323 this.scale = this.scale + 1;
30325 if(!this.zoomable()){
30330 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30331 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30336 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30343 this.scale = this.startScale;
30345 this.onRotateFail();
30350 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30352 if(this.isDocument){
30353 this.setThumbBoxSize();
30354 this.setThumbBoxPosition();
30355 this.setCanvasPosition();
30360 this.fireEvent('rotate', this, 'right');
30363 onRotateFail : function()
30365 this.errorEl.show(true);
30369 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30374 this.previewEl.dom.innerHTML = '';
30376 var canvasEl = document.createElement("canvas");
30378 var contextEl = canvasEl.getContext("2d");
30380 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30381 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30382 var center = this.imageEl.OriginWidth / 2;
30384 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30385 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30386 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30387 center = this.imageEl.OriginHeight / 2;
30390 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30392 contextEl.translate(center, center);
30393 contextEl.rotate(this.rotate * Math.PI / 180);
30395 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30397 this.canvasEl = document.createElement("canvas");
30399 this.contextEl = this.canvasEl.getContext("2d");
30401 switch (this.rotate) {
30404 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30405 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30407 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30412 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30413 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30415 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30416 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);
30420 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30425 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30426 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30428 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30429 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);
30433 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);
30438 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30439 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30441 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30442 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30446 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);
30453 this.previewEl.appendChild(this.canvasEl);
30455 this.setCanvasPosition();
30460 if(!this.canvasLoaded){
30464 var imageCanvas = document.createElement("canvas");
30466 var imageContext = imageCanvas.getContext("2d");
30468 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30469 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30471 var center = imageCanvas.width / 2;
30473 imageContext.translate(center, center);
30475 imageContext.rotate(this.rotate * Math.PI / 180);
30477 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30479 var canvas = document.createElement("canvas");
30481 var context = canvas.getContext("2d");
30483 canvas.width = this.minWidth;
30484 canvas.height = this.minHeight;
30486 switch (this.rotate) {
30489 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30490 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30492 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30493 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30495 var targetWidth = this.minWidth - 2 * x;
30496 var targetHeight = this.minHeight - 2 * y;
30500 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30501 scale = targetWidth / width;
30504 if(x > 0 && y == 0){
30505 scale = targetHeight / height;
30508 if(x > 0 && y > 0){
30509 scale = targetWidth / width;
30511 if(width < height){
30512 scale = targetHeight / height;
30516 context.scale(scale, scale);
30518 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30519 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30521 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30522 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30524 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30529 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30530 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30532 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30533 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30535 var targetWidth = this.minWidth - 2 * x;
30536 var targetHeight = this.minHeight - 2 * y;
30540 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30541 scale = targetWidth / width;
30544 if(x > 0 && y == 0){
30545 scale = targetHeight / height;
30548 if(x > 0 && y > 0){
30549 scale = targetWidth / width;
30551 if(width < height){
30552 scale = targetHeight / height;
30556 context.scale(scale, scale);
30558 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30559 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30561 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30562 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30564 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30566 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30571 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30572 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30574 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30575 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30577 var targetWidth = this.minWidth - 2 * x;
30578 var targetHeight = this.minHeight - 2 * y;
30582 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30583 scale = targetWidth / width;
30586 if(x > 0 && y == 0){
30587 scale = targetHeight / height;
30590 if(x > 0 && y > 0){
30591 scale = targetWidth / width;
30593 if(width < height){
30594 scale = targetHeight / height;
30598 context.scale(scale, scale);
30600 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30601 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30603 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30604 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30606 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30607 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30609 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30614 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30615 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30617 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30618 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30620 var targetWidth = this.minWidth - 2 * x;
30621 var targetHeight = this.minHeight - 2 * y;
30625 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30626 scale = targetWidth / width;
30629 if(x > 0 && y == 0){
30630 scale = targetHeight / height;
30633 if(x > 0 && y > 0){
30634 scale = targetWidth / width;
30636 if(width < height){
30637 scale = targetHeight / height;
30641 context.scale(scale, scale);
30643 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30644 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30646 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30647 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30649 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30651 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30658 this.cropData = canvas.toDataURL(this.cropType);
30660 if(this.fireEvent('crop', this, this.cropData) !== false){
30661 this.process(this.file, this.cropData);
30668 setThumbBoxSize : function()
30672 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30673 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30674 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30676 this.minWidth = width;
30677 this.minHeight = height;
30679 if(this.rotate == 90 || this.rotate == 270){
30680 this.minWidth = height;
30681 this.minHeight = width;
30686 width = Math.ceil(this.minWidth * height / this.minHeight);
30688 if(this.minWidth > this.minHeight){
30690 height = Math.ceil(this.minHeight * width / this.minWidth);
30693 this.thumbEl.setStyle({
30694 width : width + 'px',
30695 height : height + 'px'
30702 setThumbBoxPosition : function()
30704 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30705 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30707 this.thumbEl.setLeft(x);
30708 this.thumbEl.setTop(y);
30712 baseRotateLevel : function()
30714 this.baseRotate = 1;
30717 typeof(this.exif) != 'undefined' &&
30718 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30719 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30721 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30724 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30728 baseScaleLevel : function()
30732 if(this.isDocument){
30734 if(this.baseRotate == 6 || this.baseRotate == 8){
30736 height = this.thumbEl.getHeight();
30737 this.baseScale = height / this.imageEl.OriginWidth;
30739 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30740 width = this.thumbEl.getWidth();
30741 this.baseScale = width / this.imageEl.OriginHeight;
30747 height = this.thumbEl.getHeight();
30748 this.baseScale = height / this.imageEl.OriginHeight;
30750 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30751 width = this.thumbEl.getWidth();
30752 this.baseScale = width / this.imageEl.OriginWidth;
30758 if(this.baseRotate == 6 || this.baseRotate == 8){
30760 width = this.thumbEl.getHeight();
30761 this.baseScale = width / this.imageEl.OriginHeight;
30763 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30764 height = this.thumbEl.getWidth();
30765 this.baseScale = height / this.imageEl.OriginHeight;
30768 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30769 height = this.thumbEl.getWidth();
30770 this.baseScale = height / this.imageEl.OriginHeight;
30772 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30773 width = this.thumbEl.getHeight();
30774 this.baseScale = width / this.imageEl.OriginWidth;
30781 width = this.thumbEl.getWidth();
30782 this.baseScale = width / this.imageEl.OriginWidth;
30784 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30785 height = this.thumbEl.getHeight();
30786 this.baseScale = height / this.imageEl.OriginHeight;
30789 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30791 height = this.thumbEl.getHeight();
30792 this.baseScale = height / this.imageEl.OriginHeight;
30794 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30795 width = this.thumbEl.getWidth();
30796 this.baseScale = width / this.imageEl.OriginWidth;
30804 getScaleLevel : function()
30806 return this.baseScale * Math.pow(1.1, this.scale);
30809 onTouchStart : function(e)
30811 if(!this.canvasLoaded){
30812 this.beforeSelectFile(e);
30816 var touches = e.browserEvent.touches;
30822 if(touches.length == 1){
30823 this.onMouseDown(e);
30827 if(touches.length != 2){
30833 for(var i = 0, finger; finger = touches[i]; i++){
30834 coords.push(finger.pageX, finger.pageY);
30837 var x = Math.pow(coords[0] - coords[2], 2);
30838 var y = Math.pow(coords[1] - coords[3], 2);
30840 this.startDistance = Math.sqrt(x + y);
30842 this.startScale = this.scale;
30844 this.pinching = true;
30845 this.dragable = false;
30849 onTouchMove : function(e)
30851 if(!this.pinching && !this.dragable){
30855 var touches = e.browserEvent.touches;
30862 this.onMouseMove(e);
30868 for(var i = 0, finger; finger = touches[i]; i++){
30869 coords.push(finger.pageX, finger.pageY);
30872 var x = Math.pow(coords[0] - coords[2], 2);
30873 var y = Math.pow(coords[1] - coords[3], 2);
30875 this.endDistance = Math.sqrt(x + y);
30877 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30879 if(!this.zoomable()){
30880 this.scale = this.startScale;
30888 onTouchEnd : function(e)
30890 this.pinching = false;
30891 this.dragable = false;
30895 process : function(file, crop)
30898 this.maskEl.mask(this.loadingText);
30901 this.xhr = new XMLHttpRequest();
30903 file.xhr = this.xhr;
30905 this.xhr.open(this.method, this.url, true);
30908 "Accept": "application/json",
30909 "Cache-Control": "no-cache",
30910 "X-Requested-With": "XMLHttpRequest"
30913 for (var headerName in headers) {
30914 var headerValue = headers[headerName];
30916 this.xhr.setRequestHeader(headerName, headerValue);
30922 this.xhr.onload = function()
30924 _this.xhrOnLoad(_this.xhr);
30927 this.xhr.onerror = function()
30929 _this.xhrOnError(_this.xhr);
30932 var formData = new FormData();
30934 formData.append('returnHTML', 'NO');
30937 formData.append('crop', crop);
30940 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30941 formData.append(this.paramName, file, file.name);
30944 if(typeof(file.filename) != 'undefined'){
30945 formData.append('filename', file.filename);
30948 if(typeof(file.mimetype) != 'undefined'){
30949 formData.append('mimetype', file.mimetype);
30952 if(this.fireEvent('arrange', this, formData) != false){
30953 this.xhr.send(formData);
30957 xhrOnLoad : function(xhr)
30960 this.maskEl.unmask();
30963 if (xhr.readyState !== 4) {
30964 this.fireEvent('exception', this, xhr);
30968 var response = Roo.decode(xhr.responseText);
30970 if(!response.success){
30971 this.fireEvent('exception', this, xhr);
30975 var response = Roo.decode(xhr.responseText);
30977 this.fireEvent('upload', this, response);
30981 xhrOnError : function()
30984 this.maskEl.unmask();
30987 Roo.log('xhr on error');
30989 var response = Roo.decode(xhr.responseText);
30995 prepare : function(file)
30998 this.maskEl.mask(this.loadingText);
31004 if(typeof(file) === 'string'){
31005 this.loadCanvas(file);
31009 if(!file || !this.urlAPI){
31014 this.cropType = file.type;
31018 if(this.fireEvent('prepare', this, this.file) != false){
31020 var reader = new FileReader();
31022 reader.onload = function (e) {
31023 if (e.target.error) {
31024 Roo.log(e.target.error);
31028 var buffer = e.target.result,
31029 dataView = new DataView(buffer),
31031 maxOffset = dataView.byteLength - 4,
31035 if (dataView.getUint16(0) === 0xffd8) {
31036 while (offset < maxOffset) {
31037 markerBytes = dataView.getUint16(offset);
31039 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31040 markerLength = dataView.getUint16(offset + 2) + 2;
31041 if (offset + markerLength > dataView.byteLength) {
31042 Roo.log('Invalid meta data: Invalid segment size.');
31046 if(markerBytes == 0xffe1){
31047 _this.parseExifData(
31054 offset += markerLength;
31064 var url = _this.urlAPI.createObjectURL(_this.file);
31066 _this.loadCanvas(url);
31071 reader.readAsArrayBuffer(this.file);
31077 parseExifData : function(dataView, offset, length)
31079 var tiffOffset = offset + 10,
31083 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31084 // No Exif data, might be XMP data instead
31088 // Check for the ASCII code for "Exif" (0x45786966):
31089 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31090 // No Exif data, might be XMP data instead
31093 if (tiffOffset + 8 > dataView.byteLength) {
31094 Roo.log('Invalid Exif data: Invalid segment size.');
31097 // Check for the two null bytes:
31098 if (dataView.getUint16(offset + 8) !== 0x0000) {
31099 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31102 // Check the byte alignment:
31103 switch (dataView.getUint16(tiffOffset)) {
31105 littleEndian = true;
31108 littleEndian = false;
31111 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31114 // Check for the TIFF tag marker (0x002A):
31115 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31116 Roo.log('Invalid Exif data: Missing TIFF marker.');
31119 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31120 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31122 this.parseExifTags(
31125 tiffOffset + dirOffset,
31130 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31135 if (dirOffset + 6 > dataView.byteLength) {
31136 Roo.log('Invalid Exif data: Invalid directory offset.');
31139 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31140 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31141 if (dirEndOffset + 4 > dataView.byteLength) {
31142 Roo.log('Invalid Exif data: Invalid directory size.');
31145 for (i = 0; i < tagsNumber; i += 1) {
31149 dirOffset + 2 + 12 * i, // tag offset
31153 // Return the offset to the next directory:
31154 return dataView.getUint32(dirEndOffset, littleEndian);
31157 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31159 var tag = dataView.getUint16(offset, littleEndian);
31161 this.exif[tag] = this.getExifValue(
31165 dataView.getUint16(offset + 2, littleEndian), // tag type
31166 dataView.getUint32(offset + 4, littleEndian), // tag length
31171 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31173 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31182 Roo.log('Invalid Exif data: Invalid tag type.');
31186 tagSize = tagType.size * length;
31187 // Determine if the value is contained in the dataOffset bytes,
31188 // or if the value at the dataOffset is a pointer to the actual data:
31189 dataOffset = tagSize > 4 ?
31190 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31191 if (dataOffset + tagSize > dataView.byteLength) {
31192 Roo.log('Invalid Exif data: Invalid data offset.');
31195 if (length === 1) {
31196 return tagType.getValue(dataView, dataOffset, littleEndian);
31199 for (i = 0; i < length; i += 1) {
31200 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31203 if (tagType.ascii) {
31205 // Concatenate the chars:
31206 for (i = 0; i < values.length; i += 1) {
31208 // Ignore the terminating NULL byte(s):
31209 if (c === '\u0000') {
31221 Roo.apply(Roo.bootstrap.UploadCropbox, {
31223 'Orientation': 0x0112
31227 1: 0, //'top-left',
31229 3: 180, //'bottom-right',
31230 // 4: 'bottom-left',
31232 6: 90, //'right-top',
31233 // 7: 'right-bottom',
31234 8: 270 //'left-bottom'
31238 // byte, 8-bit unsigned int:
31240 getValue: function (dataView, dataOffset) {
31241 return dataView.getUint8(dataOffset);
31245 // ascii, 8-bit byte:
31247 getValue: function (dataView, dataOffset) {
31248 return String.fromCharCode(dataView.getUint8(dataOffset));
31253 // short, 16 bit int:
31255 getValue: function (dataView, dataOffset, littleEndian) {
31256 return dataView.getUint16(dataOffset, littleEndian);
31260 // long, 32 bit int:
31262 getValue: function (dataView, dataOffset, littleEndian) {
31263 return dataView.getUint32(dataOffset, littleEndian);
31267 // rational = two long values, first is numerator, second is denominator:
31269 getValue: function (dataView, dataOffset, littleEndian) {
31270 return dataView.getUint32(dataOffset, littleEndian) /
31271 dataView.getUint32(dataOffset + 4, littleEndian);
31275 // slong, 32 bit signed int:
31277 getValue: function (dataView, dataOffset, littleEndian) {
31278 return dataView.getInt32(dataOffset, littleEndian);
31282 // srational, two slongs, first is numerator, second is denominator:
31284 getValue: function (dataView, dataOffset, littleEndian) {
31285 return dataView.getInt32(dataOffset, littleEndian) /
31286 dataView.getInt32(dataOffset + 4, littleEndian);
31296 cls : 'btn-group roo-upload-cropbox-rotate-left',
31297 action : 'rotate-left',
31301 cls : 'btn btn-default',
31302 html : '<i class="fa fa-undo"></i>'
31308 cls : 'btn-group roo-upload-cropbox-picture',
31309 action : 'picture',
31313 cls : 'btn btn-default',
31314 html : '<i class="fa fa-picture-o"></i>'
31320 cls : 'btn-group roo-upload-cropbox-rotate-right',
31321 action : 'rotate-right',
31325 cls : 'btn btn-default',
31326 html : '<i class="fa fa-repeat"></i>'
31334 cls : 'btn-group roo-upload-cropbox-rotate-left',
31335 action : 'rotate-left',
31339 cls : 'btn btn-default',
31340 html : '<i class="fa fa-undo"></i>'
31346 cls : 'btn-group roo-upload-cropbox-download',
31347 action : 'download',
31351 cls : 'btn btn-default',
31352 html : '<i class="fa fa-download"></i>'
31358 cls : 'btn-group roo-upload-cropbox-crop',
31363 cls : 'btn btn-default',
31364 html : '<i class="fa fa-crop"></i>'
31370 cls : 'btn-group roo-upload-cropbox-trash',
31375 cls : 'btn btn-default',
31376 html : '<i class="fa fa-trash"></i>'
31382 cls : 'btn-group roo-upload-cropbox-rotate-right',
31383 action : 'rotate-right',
31387 cls : 'btn btn-default',
31388 html : '<i class="fa fa-repeat"></i>'
31396 cls : 'btn-group roo-upload-cropbox-rotate-left',
31397 action : 'rotate-left',
31401 cls : 'btn btn-default',
31402 html : '<i class="fa fa-undo"></i>'
31408 cls : 'btn-group roo-upload-cropbox-rotate-right',
31409 action : 'rotate-right',
31413 cls : 'btn btn-default',
31414 html : '<i class="fa fa-repeat"></i>'
31427 * @class Roo.bootstrap.DocumentManager
31428 * @extends Roo.bootstrap.Component
31429 * Bootstrap DocumentManager class
31430 * @cfg {String} paramName default 'imageUpload'
31431 * @cfg {String} toolTipName default 'filename'
31432 * @cfg {String} method default POST
31433 * @cfg {String} url action url
31434 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31435 * @cfg {Boolean} multiple multiple upload default true
31436 * @cfg {Number} thumbSize default 300
31437 * @cfg {String} fieldLabel
31438 * @cfg {Number} labelWidth default 4
31439 * @cfg {String} labelAlign (left|top) default left
31440 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31441 * @cfg {Number} labellg set the width of label (1-12)
31442 * @cfg {Number} labelmd set the width of label (1-12)
31443 * @cfg {Number} labelsm set the width of label (1-12)
31444 * @cfg {Number} labelxs set the width of label (1-12)
31447 * Create a new DocumentManager
31448 * @param {Object} config The config object
31451 Roo.bootstrap.DocumentManager = function(config){
31452 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31455 this.delegates = [];
31460 * Fire when initial the DocumentManager
31461 * @param {Roo.bootstrap.DocumentManager} this
31466 * inspect selected file
31467 * @param {Roo.bootstrap.DocumentManager} this
31468 * @param {File} file
31473 * Fire when xhr load exception
31474 * @param {Roo.bootstrap.DocumentManager} this
31475 * @param {XMLHttpRequest} xhr
31477 "exception" : true,
31479 * @event afterupload
31480 * Fire when xhr load exception
31481 * @param {Roo.bootstrap.DocumentManager} this
31482 * @param {XMLHttpRequest} xhr
31484 "afterupload" : true,
31487 * prepare the form data
31488 * @param {Roo.bootstrap.DocumentManager} this
31489 * @param {Object} formData
31494 * Fire when remove the file
31495 * @param {Roo.bootstrap.DocumentManager} this
31496 * @param {Object} file
31501 * Fire after refresh the file
31502 * @param {Roo.bootstrap.DocumentManager} this
31507 * Fire after click the image
31508 * @param {Roo.bootstrap.DocumentManager} this
31509 * @param {Object} file
31514 * Fire when upload a image and editable set to true
31515 * @param {Roo.bootstrap.DocumentManager} this
31516 * @param {Object} file
31520 * @event beforeselectfile
31521 * Fire before select file
31522 * @param {Roo.bootstrap.DocumentManager} this
31524 "beforeselectfile" : true,
31527 * Fire before process file
31528 * @param {Roo.bootstrap.DocumentManager} this
31529 * @param {Object} file
31533 * @event previewrendered
31534 * Fire when preview rendered
31535 * @param {Roo.bootstrap.DocumentManager} this
31536 * @param {Object} file
31538 "previewrendered" : true,
31541 "previewResize" : true
31546 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31555 paramName : 'imageUpload',
31556 toolTipName : 'filename',
31559 labelAlign : 'left',
31569 getAutoCreate : function()
31571 var managerWidget = {
31573 cls : 'roo-document-manager',
31577 cls : 'roo-document-manager-selector',
31582 cls : 'roo-document-manager-uploader',
31586 cls : 'roo-document-manager-upload-btn',
31587 html : '<i class="fa fa-plus"></i>'
31598 cls : 'column col-md-12',
31603 if(this.fieldLabel.length){
31608 cls : 'column col-md-12',
31609 html : this.fieldLabel
31613 cls : 'column col-md-12',
31618 if(this.labelAlign == 'left'){
31623 html : this.fieldLabel
31632 if(this.labelWidth > 12){
31633 content[0].style = "width: " + this.labelWidth + 'px';
31636 if(this.labelWidth < 13 && this.labelmd == 0){
31637 this.labelmd = this.labelWidth;
31640 if(this.labellg > 0){
31641 content[0].cls += ' col-lg-' + this.labellg;
31642 content[1].cls += ' col-lg-' + (12 - this.labellg);
31645 if(this.labelmd > 0){
31646 content[0].cls += ' col-md-' + this.labelmd;
31647 content[1].cls += ' col-md-' + (12 - this.labelmd);
31650 if(this.labelsm > 0){
31651 content[0].cls += ' col-sm-' + this.labelsm;
31652 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31655 if(this.labelxs > 0){
31656 content[0].cls += ' col-xs-' + this.labelxs;
31657 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31665 cls : 'row clearfix',
31673 initEvents : function()
31675 this.managerEl = this.el.select('.roo-document-manager', true).first();
31676 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31678 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31679 this.selectorEl.hide();
31682 this.selectorEl.attr('multiple', 'multiple');
31685 this.selectorEl.on('change', this.onFileSelected, this);
31687 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31688 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31690 this.uploader.on('click', this.onUploaderClick, this);
31692 this.renderProgressDialog();
31696 window.addEventListener("resize", function() { _this.refresh(); } );
31698 this.fireEvent('initial', this);
31701 renderProgressDialog : function()
31705 this.progressDialog = new Roo.bootstrap.Modal({
31706 cls : 'roo-document-manager-progress-dialog',
31707 allow_close : false,
31718 btnclick : function() {
31719 _this.uploadCancel();
31725 this.progressDialog.render(Roo.get(document.body));
31727 this.progress = new Roo.bootstrap.Progress({
31728 cls : 'roo-document-manager-progress',
31733 this.progress.render(this.progressDialog.getChildContainer());
31735 this.progressBar = new Roo.bootstrap.ProgressBar({
31736 cls : 'roo-document-manager-progress-bar',
31739 aria_valuemax : 12,
31743 this.progressBar.render(this.progress.getChildContainer());
31746 onUploaderClick : function(e)
31748 e.preventDefault();
31750 if(this.fireEvent('beforeselectfile', this) != false){
31751 this.selectorEl.dom.click();
31756 onFileSelected : function(e)
31758 e.preventDefault();
31760 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31764 Roo.each(this.selectorEl.dom.files, function(file){
31765 if(this.fireEvent('inspect', this, file) != false){
31766 this.files.push(file);
31776 this.selectorEl.dom.value = '';
31778 if(!this.files || !this.files.length){
31782 if(this.boxes > 0 && this.files.length > this.boxes){
31783 this.files = this.files.slice(0, this.boxes);
31786 this.uploader.show();
31788 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31789 this.uploader.hide();
31798 Roo.each(this.files, function(file){
31800 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31801 var f = this.renderPreview(file);
31806 if(file.type.indexOf('image') != -1){
31807 this.delegates.push(
31809 _this.process(file);
31810 }).createDelegate(this)
31818 _this.process(file);
31819 }).createDelegate(this)
31824 this.files = files;
31826 this.delegates = this.delegates.concat(docs);
31828 if(!this.delegates.length){
31833 this.progressBar.aria_valuemax = this.delegates.length;
31840 arrange : function()
31842 if(!this.delegates.length){
31843 this.progressDialog.hide();
31848 var delegate = this.delegates.shift();
31850 this.progressDialog.show();
31852 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31854 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31859 refresh : function()
31861 this.uploader.show();
31863 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31864 this.uploader.hide();
31867 Roo.isTouch ? this.closable(false) : this.closable(true);
31869 this.fireEvent('refresh', this);
31872 onRemove : function(e, el, o)
31874 e.preventDefault();
31876 this.fireEvent('remove', this, o);
31880 remove : function(o)
31884 Roo.each(this.files, function(file){
31885 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31894 this.files = files;
31901 Roo.each(this.files, function(file){
31906 file.target.remove();
31915 onClick : function(e, el, o)
31917 e.preventDefault();
31919 this.fireEvent('click', this, o);
31923 closable : function(closable)
31925 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31927 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31939 xhrOnLoad : function(xhr)
31941 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31945 if (xhr.readyState !== 4) {
31947 this.fireEvent('exception', this, xhr);
31951 var response = Roo.decode(xhr.responseText);
31953 if(!response.success){
31955 this.fireEvent('exception', this, xhr);
31959 var file = this.renderPreview(response.data);
31961 this.files.push(file);
31965 this.fireEvent('afterupload', this, xhr);
31969 xhrOnError : function(xhr)
31971 Roo.log('xhr on error');
31973 var response = Roo.decode(xhr.responseText);
31980 process : function(file)
31982 if(this.fireEvent('process', this, file) !== false){
31983 if(this.editable && file.type.indexOf('image') != -1){
31984 this.fireEvent('edit', this, file);
31988 this.uploadStart(file, false);
31995 uploadStart : function(file, crop)
31997 this.xhr = new XMLHttpRequest();
31999 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32004 file.xhr = this.xhr;
32006 this.managerEl.createChild({
32008 cls : 'roo-document-manager-loading',
32012 tooltip : file.name,
32013 cls : 'roo-document-manager-thumb',
32014 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32020 this.xhr.open(this.method, this.url, true);
32023 "Accept": "application/json",
32024 "Cache-Control": "no-cache",
32025 "X-Requested-With": "XMLHttpRequest"
32028 for (var headerName in headers) {
32029 var headerValue = headers[headerName];
32031 this.xhr.setRequestHeader(headerName, headerValue);
32037 this.xhr.onload = function()
32039 _this.xhrOnLoad(_this.xhr);
32042 this.xhr.onerror = function()
32044 _this.xhrOnError(_this.xhr);
32047 var formData = new FormData();
32049 formData.append('returnHTML', 'NO');
32052 formData.append('crop', crop);
32055 formData.append(this.paramName, file, file.name);
32062 if(this.fireEvent('prepare', this, formData, options) != false){
32064 if(options.manually){
32068 this.xhr.send(formData);
32072 this.uploadCancel();
32075 uploadCancel : function()
32081 this.delegates = [];
32083 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32090 renderPreview : function(file)
32092 if(typeof(file.target) != 'undefined' && file.target){
32096 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32098 var previewEl = this.managerEl.createChild({
32100 cls : 'roo-document-manager-preview',
32104 tooltip : file[this.toolTipName],
32105 cls : 'roo-document-manager-thumb',
32106 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32111 html : '<i class="fa fa-times-circle"></i>'
32116 var close = previewEl.select('button.close', true).first();
32118 close.on('click', this.onRemove, this, file);
32120 file.target = previewEl;
32122 var image = previewEl.select('img', true).first();
32126 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32128 image.on('click', this.onClick, this, file);
32130 this.fireEvent('previewrendered', this, file);
32136 onPreviewLoad : function(file, image)
32138 if(typeof(file.target) == 'undefined' || !file.target){
32142 var width = image.dom.naturalWidth || image.dom.width;
32143 var height = image.dom.naturalHeight || image.dom.height;
32145 if(!this.previewResize) {
32149 if(width > height){
32150 file.target.addClass('wide');
32154 file.target.addClass('tall');
32159 uploadFromSource : function(file, crop)
32161 this.xhr = new XMLHttpRequest();
32163 this.managerEl.createChild({
32165 cls : 'roo-document-manager-loading',
32169 tooltip : file.name,
32170 cls : 'roo-document-manager-thumb',
32171 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32177 this.xhr.open(this.method, this.url, true);
32180 "Accept": "application/json",
32181 "Cache-Control": "no-cache",
32182 "X-Requested-With": "XMLHttpRequest"
32185 for (var headerName in headers) {
32186 var headerValue = headers[headerName];
32188 this.xhr.setRequestHeader(headerName, headerValue);
32194 this.xhr.onload = function()
32196 _this.xhrOnLoad(_this.xhr);
32199 this.xhr.onerror = function()
32201 _this.xhrOnError(_this.xhr);
32204 var formData = new FormData();
32206 formData.append('returnHTML', 'NO');
32208 formData.append('crop', crop);
32210 if(typeof(file.filename) != 'undefined'){
32211 formData.append('filename', file.filename);
32214 if(typeof(file.mimetype) != 'undefined'){
32215 formData.append('mimetype', file.mimetype);
32220 if(this.fireEvent('prepare', this, formData) != false){
32221 this.xhr.send(formData);
32231 * @class Roo.bootstrap.DocumentViewer
32232 * @extends Roo.bootstrap.Component
32233 * Bootstrap DocumentViewer class
32234 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32235 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32238 * Create a new DocumentViewer
32239 * @param {Object} config The config object
32242 Roo.bootstrap.DocumentViewer = function(config){
32243 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32248 * Fire after initEvent
32249 * @param {Roo.bootstrap.DocumentViewer} this
32255 * @param {Roo.bootstrap.DocumentViewer} this
32260 * Fire after download button
32261 * @param {Roo.bootstrap.DocumentViewer} this
32266 * Fire after trash button
32267 * @param {Roo.bootstrap.DocumentViewer} this
32274 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32276 showDownload : true,
32280 getAutoCreate : function()
32284 cls : 'roo-document-viewer',
32288 cls : 'roo-document-viewer-body',
32292 cls : 'roo-document-viewer-thumb',
32296 cls : 'roo-document-viewer-image'
32304 cls : 'roo-document-viewer-footer',
32307 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32311 cls : 'btn-group roo-document-viewer-download',
32315 cls : 'btn btn-default',
32316 html : '<i class="fa fa-download"></i>'
32322 cls : 'btn-group roo-document-viewer-trash',
32326 cls : 'btn btn-default',
32327 html : '<i class="fa fa-trash"></i>'
32340 initEvents : function()
32342 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32343 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32345 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32346 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32348 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32349 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32351 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32352 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32354 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32355 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32357 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32358 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32360 this.bodyEl.on('click', this.onClick, this);
32361 this.downloadBtn.on('click', this.onDownload, this);
32362 this.trashBtn.on('click', this.onTrash, this);
32364 this.downloadBtn.hide();
32365 this.trashBtn.hide();
32367 if(this.showDownload){
32368 this.downloadBtn.show();
32371 if(this.showTrash){
32372 this.trashBtn.show();
32375 if(!this.showDownload && !this.showTrash) {
32376 this.footerEl.hide();
32381 initial : function()
32383 this.fireEvent('initial', this);
32387 onClick : function(e)
32389 e.preventDefault();
32391 this.fireEvent('click', this);
32394 onDownload : function(e)
32396 e.preventDefault();
32398 this.fireEvent('download', this);
32401 onTrash : function(e)
32403 e.preventDefault();
32405 this.fireEvent('trash', this);
32417 * @class Roo.bootstrap.NavProgressBar
32418 * @extends Roo.bootstrap.Component
32419 * Bootstrap NavProgressBar class
32422 * Create a new nav progress bar
32423 * @param {Object} config The config object
32426 Roo.bootstrap.NavProgressBar = function(config){
32427 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32429 this.bullets = this.bullets || [];
32431 // Roo.bootstrap.NavProgressBar.register(this);
32435 * Fires when the active item changes
32436 * @param {Roo.bootstrap.NavProgressBar} this
32437 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32438 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32445 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32450 getAutoCreate : function()
32452 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32456 cls : 'roo-navigation-bar-group',
32460 cls : 'roo-navigation-top-bar'
32464 cls : 'roo-navigation-bullets-bar',
32468 cls : 'roo-navigation-bar'
32475 cls : 'roo-navigation-bottom-bar'
32485 initEvents: function()
32490 onRender : function(ct, position)
32492 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32494 if(this.bullets.length){
32495 Roo.each(this.bullets, function(b){
32504 addItem : function(cfg)
32506 var item = new Roo.bootstrap.NavProgressItem(cfg);
32508 item.parentId = this.id;
32509 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32512 var top = new Roo.bootstrap.Element({
32514 cls : 'roo-navigation-bar-text'
32517 var bottom = new Roo.bootstrap.Element({
32519 cls : 'roo-navigation-bar-text'
32522 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32523 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32525 var topText = new Roo.bootstrap.Element({
32527 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32530 var bottomText = new Roo.bootstrap.Element({
32532 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32535 topText.onRender(top.el, null);
32536 bottomText.onRender(bottom.el, null);
32539 item.bottomEl = bottom;
32542 this.barItems.push(item);
32547 getActive : function()
32549 var active = false;
32551 Roo.each(this.barItems, function(v){
32553 if (!v.isActive()) {
32565 setActiveItem : function(item)
32569 Roo.each(this.barItems, function(v){
32570 if (v.rid == item.rid) {
32574 if (v.isActive()) {
32575 v.setActive(false);
32580 item.setActive(true);
32582 this.fireEvent('changed', this, item, prev);
32585 getBarItem: function(rid)
32589 Roo.each(this.barItems, function(e) {
32590 if (e.rid != rid) {
32601 indexOfItem : function(item)
32605 Roo.each(this.barItems, function(v, i){
32607 if (v.rid != item.rid) {
32618 setActiveNext : function()
32620 var i = this.indexOfItem(this.getActive());
32622 if (i > this.barItems.length) {
32626 this.setActiveItem(this.barItems[i+1]);
32629 setActivePrev : function()
32631 var i = this.indexOfItem(this.getActive());
32637 this.setActiveItem(this.barItems[i-1]);
32640 format : function()
32642 if(!this.barItems.length){
32646 var width = 100 / this.barItems.length;
32648 Roo.each(this.barItems, function(i){
32649 i.el.setStyle('width', width + '%');
32650 i.topEl.el.setStyle('width', width + '%');
32651 i.bottomEl.el.setStyle('width', width + '%');
32660 * Nav Progress Item
32665 * @class Roo.bootstrap.NavProgressItem
32666 * @extends Roo.bootstrap.Component
32667 * Bootstrap NavProgressItem class
32668 * @cfg {String} rid the reference id
32669 * @cfg {Boolean} active (true|false) Is item active default false
32670 * @cfg {Boolean} disabled (true|false) Is item active default false
32671 * @cfg {String} html
32672 * @cfg {String} position (top|bottom) text position default bottom
32673 * @cfg {String} icon show icon instead of number
32676 * Create a new NavProgressItem
32677 * @param {Object} config The config object
32679 Roo.bootstrap.NavProgressItem = function(config){
32680 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32685 * The raw click event for the entire grid.
32686 * @param {Roo.bootstrap.NavProgressItem} this
32687 * @param {Roo.EventObject} e
32694 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32700 position : 'bottom',
32703 getAutoCreate : function()
32705 var iconCls = 'roo-navigation-bar-item-icon';
32707 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32711 cls: 'roo-navigation-bar-item',
32721 cfg.cls += ' active';
32724 cfg.cls += ' disabled';
32730 disable : function()
32732 this.setDisabled(true);
32735 enable : function()
32737 this.setDisabled(false);
32740 initEvents: function()
32742 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32744 this.iconEl.on('click', this.onClick, this);
32747 onClick : function(e)
32749 e.preventDefault();
32755 if(this.fireEvent('click', this, e) === false){
32759 this.parent().setActiveItem(this);
32762 isActive: function ()
32764 return this.active;
32767 setActive : function(state)
32769 if(this.active == state){
32773 this.active = state;
32776 this.el.addClass('active');
32780 this.el.removeClass('active');
32785 setDisabled : function(state)
32787 if(this.disabled == state){
32791 this.disabled = state;
32794 this.el.addClass('disabled');
32798 this.el.removeClass('disabled');
32801 tooltipEl : function()
32803 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32816 * @class Roo.bootstrap.FieldLabel
32817 * @extends Roo.bootstrap.Component
32818 * Bootstrap FieldLabel class
32819 * @cfg {String} html contents of the element
32820 * @cfg {String} tag tag of the element default label
32821 * @cfg {String} cls class of the element
32822 * @cfg {String} target label target
32823 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32824 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32825 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32826 * @cfg {String} iconTooltip default "This field is required"
32827 * @cfg {String} indicatorpos (left|right) default left
32830 * Create a new FieldLabel
32831 * @param {Object} config The config object
32834 Roo.bootstrap.FieldLabel = function(config){
32835 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32840 * Fires after the field has been marked as invalid.
32841 * @param {Roo.form.FieldLabel} this
32842 * @param {String} msg The validation message
32847 * Fires after the field has been validated with no errors.
32848 * @param {Roo.form.FieldLabel} this
32854 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32861 invalidClass : 'has-warning',
32862 validClass : 'has-success',
32863 iconTooltip : 'This field is required',
32864 indicatorpos : 'left',
32866 getAutoCreate : function(){
32869 if (!this.allowBlank) {
32875 cls : 'roo-bootstrap-field-label ' + this.cls,
32880 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32881 tooltip : this.iconTooltip
32890 if(this.indicatorpos == 'right'){
32893 cls : 'roo-bootstrap-field-label ' + this.cls,
32902 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32903 tooltip : this.iconTooltip
32912 initEvents: function()
32914 Roo.bootstrap.Element.superclass.initEvents.call(this);
32916 this.indicator = this.indicatorEl();
32918 if(this.indicator){
32919 this.indicator.removeClass('visible');
32920 this.indicator.addClass('invisible');
32923 Roo.bootstrap.FieldLabel.register(this);
32926 indicatorEl : function()
32928 var indicator = this.el.select('i.roo-required-indicator',true).first();
32939 * Mark this field as valid
32941 markValid : function()
32943 if(this.indicator){
32944 this.indicator.removeClass('visible');
32945 this.indicator.addClass('invisible');
32947 if (Roo.bootstrap.version == 3) {
32948 this.el.removeClass(this.invalidClass);
32949 this.el.addClass(this.validClass);
32951 this.el.removeClass('is-invalid');
32952 this.el.addClass('is-valid');
32956 this.fireEvent('valid', this);
32960 * Mark this field as invalid
32961 * @param {String} msg The validation message
32963 markInvalid : function(msg)
32965 if(this.indicator){
32966 this.indicator.removeClass('invisible');
32967 this.indicator.addClass('visible');
32969 if (Roo.bootstrap.version == 3) {
32970 this.el.removeClass(this.validClass);
32971 this.el.addClass(this.invalidClass);
32973 this.el.removeClass('is-valid');
32974 this.el.addClass('is-invalid');
32978 this.fireEvent('invalid', this, msg);
32984 Roo.apply(Roo.bootstrap.FieldLabel, {
32989 * register a FieldLabel Group
32990 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32992 register : function(label)
32994 if(this.groups.hasOwnProperty(label.target)){
32998 this.groups[label.target] = label;
33002 * fetch a FieldLabel Group based on the target
33003 * @param {string} target
33004 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33006 get: function(target) {
33007 if (typeof(this.groups[target]) == 'undefined') {
33011 return this.groups[target] ;
33020 * page DateSplitField.
33026 * @class Roo.bootstrap.DateSplitField
33027 * @extends Roo.bootstrap.Component
33028 * Bootstrap DateSplitField class
33029 * @cfg {string} fieldLabel - the label associated
33030 * @cfg {Number} labelWidth set the width of label (0-12)
33031 * @cfg {String} labelAlign (top|left)
33032 * @cfg {Boolean} dayAllowBlank (true|false) default false
33033 * @cfg {Boolean} monthAllowBlank (true|false) default false
33034 * @cfg {Boolean} yearAllowBlank (true|false) default false
33035 * @cfg {string} dayPlaceholder
33036 * @cfg {string} monthPlaceholder
33037 * @cfg {string} yearPlaceholder
33038 * @cfg {string} dayFormat default 'd'
33039 * @cfg {string} monthFormat default 'm'
33040 * @cfg {string} yearFormat default 'Y'
33041 * @cfg {Number} labellg set the width of label (1-12)
33042 * @cfg {Number} labelmd set the width of label (1-12)
33043 * @cfg {Number} labelsm set the width of label (1-12)
33044 * @cfg {Number} labelxs set the width of label (1-12)
33048 * Create a new DateSplitField
33049 * @param {Object} config The config object
33052 Roo.bootstrap.DateSplitField = function(config){
33053 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33059 * getting the data of years
33060 * @param {Roo.bootstrap.DateSplitField} this
33061 * @param {Object} years
33066 * getting the data of days
33067 * @param {Roo.bootstrap.DateSplitField} this
33068 * @param {Object} days
33073 * Fires after the field has been marked as invalid.
33074 * @param {Roo.form.Field} this
33075 * @param {String} msg The validation message
33080 * Fires after the field has been validated with no errors.
33081 * @param {Roo.form.Field} this
33087 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33090 labelAlign : 'top',
33092 dayAllowBlank : false,
33093 monthAllowBlank : false,
33094 yearAllowBlank : false,
33095 dayPlaceholder : '',
33096 monthPlaceholder : '',
33097 yearPlaceholder : '',
33101 isFormField : true,
33107 getAutoCreate : function()
33111 cls : 'row roo-date-split-field-group',
33116 cls : 'form-hidden-field roo-date-split-field-group-value',
33122 var labelCls = 'col-md-12';
33123 var contentCls = 'col-md-4';
33125 if(this.fieldLabel){
33129 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33133 html : this.fieldLabel
33138 if(this.labelAlign == 'left'){
33140 if(this.labelWidth > 12){
33141 label.style = "width: " + this.labelWidth + 'px';
33144 if(this.labelWidth < 13 && this.labelmd == 0){
33145 this.labelmd = this.labelWidth;
33148 if(this.labellg > 0){
33149 labelCls = ' col-lg-' + this.labellg;
33150 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33153 if(this.labelmd > 0){
33154 labelCls = ' col-md-' + this.labelmd;
33155 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33158 if(this.labelsm > 0){
33159 labelCls = ' col-sm-' + this.labelsm;
33160 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33163 if(this.labelxs > 0){
33164 labelCls = ' col-xs-' + this.labelxs;
33165 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33169 label.cls += ' ' + labelCls;
33171 cfg.cn.push(label);
33174 Roo.each(['day', 'month', 'year'], function(t){
33177 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33184 inputEl: function ()
33186 return this.el.select('.roo-date-split-field-group-value', true).first();
33189 onRender : function(ct, position)
33193 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33195 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33197 this.dayField = new Roo.bootstrap.ComboBox({
33198 allowBlank : this.dayAllowBlank,
33199 alwaysQuery : true,
33200 displayField : 'value',
33203 forceSelection : true,
33205 placeholder : this.dayPlaceholder,
33206 selectOnFocus : true,
33207 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33208 triggerAction : 'all',
33210 valueField : 'value',
33211 store : new Roo.data.SimpleStore({
33212 data : (function() {
33214 _this.fireEvent('days', _this, days);
33217 fields : [ 'value' ]
33220 select : function (_self, record, index)
33222 _this.setValue(_this.getValue());
33227 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33229 this.monthField = new Roo.bootstrap.MonthField({
33230 after : '<i class=\"fa fa-calendar\"></i>',
33231 allowBlank : this.monthAllowBlank,
33232 placeholder : this.monthPlaceholder,
33235 render : function (_self)
33237 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33238 e.preventDefault();
33242 select : function (_self, oldvalue, newvalue)
33244 _this.setValue(_this.getValue());
33249 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33251 this.yearField = new Roo.bootstrap.ComboBox({
33252 allowBlank : this.yearAllowBlank,
33253 alwaysQuery : true,
33254 displayField : 'value',
33257 forceSelection : true,
33259 placeholder : this.yearPlaceholder,
33260 selectOnFocus : true,
33261 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33262 triggerAction : 'all',
33264 valueField : 'value',
33265 store : new Roo.data.SimpleStore({
33266 data : (function() {
33268 _this.fireEvent('years', _this, years);
33271 fields : [ 'value' ]
33274 select : function (_self, record, index)
33276 _this.setValue(_this.getValue());
33281 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33284 setValue : function(v, format)
33286 this.inputEl.dom.value = v;
33288 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33290 var d = Date.parseDate(v, f);
33297 this.setDay(d.format(this.dayFormat));
33298 this.setMonth(d.format(this.monthFormat));
33299 this.setYear(d.format(this.yearFormat));
33306 setDay : function(v)
33308 this.dayField.setValue(v);
33309 this.inputEl.dom.value = this.getValue();
33314 setMonth : function(v)
33316 this.monthField.setValue(v, true);
33317 this.inputEl.dom.value = this.getValue();
33322 setYear : function(v)
33324 this.yearField.setValue(v);
33325 this.inputEl.dom.value = this.getValue();
33330 getDay : function()
33332 return this.dayField.getValue();
33335 getMonth : function()
33337 return this.monthField.getValue();
33340 getYear : function()
33342 return this.yearField.getValue();
33345 getValue : function()
33347 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33349 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33359 this.inputEl.dom.value = '';
33364 validate : function()
33366 var d = this.dayField.validate();
33367 var m = this.monthField.validate();
33368 var y = this.yearField.validate();
33373 (!this.dayAllowBlank && !d) ||
33374 (!this.monthAllowBlank && !m) ||
33375 (!this.yearAllowBlank && !y)
33380 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33389 this.markInvalid();
33394 markValid : function()
33397 var label = this.el.select('label', true).first();
33398 var icon = this.el.select('i.fa-star', true).first();
33404 this.fireEvent('valid', this);
33408 * Mark this field as invalid
33409 * @param {String} msg The validation message
33411 markInvalid : function(msg)
33414 var label = this.el.select('label', true).first();
33415 var icon = this.el.select('i.fa-star', true).first();
33417 if(label && !icon){
33418 this.el.select('.roo-date-split-field-label', true).createChild({
33420 cls : 'text-danger fa fa-lg fa-star',
33421 tooltip : 'This field is required',
33422 style : 'margin-right:5px;'
33426 this.fireEvent('invalid', this, msg);
33429 clearInvalid : function()
33431 var label = this.el.select('label', true).first();
33432 var icon = this.el.select('i.fa-star', true).first();
33438 this.fireEvent('valid', this);
33441 getName: function()
33451 * http://masonry.desandro.com
33453 * The idea is to render all the bricks based on vertical width...
33455 * The original code extends 'outlayer' - we might need to use that....
33461 * @class Roo.bootstrap.LayoutMasonry
33462 * @extends Roo.bootstrap.Component
33463 * Bootstrap Layout Masonry class
33466 * Create a new Element
33467 * @param {Object} config The config object
33470 Roo.bootstrap.LayoutMasonry = function(config){
33472 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33476 Roo.bootstrap.LayoutMasonry.register(this);
33482 * Fire after layout the items
33483 * @param {Roo.bootstrap.LayoutMasonry} this
33484 * @param {Roo.EventObject} e
33491 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33494 * @cfg {Boolean} isLayoutInstant = no animation?
33496 isLayoutInstant : false, // needed?
33499 * @cfg {Number} boxWidth width of the columns
33504 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33509 * @cfg {Number} padWidth padding below box..
33514 * @cfg {Number} gutter gutter width..
33519 * @cfg {Number} maxCols maximum number of columns
33525 * @cfg {Boolean} isAutoInitial defalut true
33527 isAutoInitial : true,
33532 * @cfg {Boolean} isHorizontal defalut false
33534 isHorizontal : false,
33536 currentSize : null,
33542 bricks: null, //CompositeElement
33546 _isLayoutInited : false,
33548 // isAlternative : false, // only use for vertical layout...
33551 * @cfg {Number} alternativePadWidth padding below box..
33553 alternativePadWidth : 50,
33555 selectedBrick : [],
33557 getAutoCreate : function(){
33559 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33563 cls: 'blog-masonary-wrapper ' + this.cls,
33565 cls : 'mas-boxes masonary'
33572 getChildContainer: function( )
33574 if (this.boxesEl) {
33575 return this.boxesEl;
33578 this.boxesEl = this.el.select('.mas-boxes').first();
33580 return this.boxesEl;
33584 initEvents : function()
33588 if(this.isAutoInitial){
33589 Roo.log('hook children rendered');
33590 this.on('childrenrendered', function() {
33591 Roo.log('children rendered');
33597 initial : function()
33599 this.selectedBrick = [];
33601 this.currentSize = this.el.getBox(true);
33603 Roo.EventManager.onWindowResize(this.resize, this);
33605 if(!this.isAutoInitial){
33613 //this.layout.defer(500,this);
33617 resize : function()
33619 var cs = this.el.getBox(true);
33622 this.currentSize.width == cs.width &&
33623 this.currentSize.x == cs.x &&
33624 this.currentSize.height == cs.height &&
33625 this.currentSize.y == cs.y
33627 Roo.log("no change in with or X or Y");
33631 this.currentSize = cs;
33637 layout : function()
33639 this._resetLayout();
33641 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33643 this.layoutItems( isInstant );
33645 this._isLayoutInited = true;
33647 this.fireEvent('layout', this);
33651 _resetLayout : function()
33653 if(this.isHorizontal){
33654 this.horizontalMeasureColumns();
33658 this.verticalMeasureColumns();
33662 verticalMeasureColumns : function()
33664 this.getContainerWidth();
33666 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33667 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33671 var boxWidth = this.boxWidth + this.padWidth;
33673 if(this.containerWidth < this.boxWidth){
33674 boxWidth = this.containerWidth
33677 var containerWidth = this.containerWidth;
33679 var cols = Math.floor(containerWidth / boxWidth);
33681 this.cols = Math.max( cols, 1 );
33683 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33685 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33687 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33689 this.colWidth = boxWidth + avail - this.padWidth;
33691 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33692 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33695 horizontalMeasureColumns : function()
33697 this.getContainerWidth();
33699 var boxWidth = this.boxWidth;
33701 if(this.containerWidth < boxWidth){
33702 boxWidth = this.containerWidth;
33705 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33707 this.el.setHeight(boxWidth);
33711 getContainerWidth : function()
33713 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33716 layoutItems : function( isInstant )
33718 Roo.log(this.bricks);
33720 var items = Roo.apply([], this.bricks);
33722 if(this.isHorizontal){
33723 this._horizontalLayoutItems( items , isInstant );
33727 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33728 // this._verticalAlternativeLayoutItems( items , isInstant );
33732 this._verticalLayoutItems( items , isInstant );
33736 _verticalLayoutItems : function ( items , isInstant)
33738 if ( !items || !items.length ) {
33743 ['xs', 'xs', 'xs', 'tall'],
33744 ['xs', 'xs', 'tall'],
33745 ['xs', 'xs', 'sm'],
33746 ['xs', 'xs', 'xs'],
33752 ['sm', 'xs', 'xs'],
33756 ['tall', 'xs', 'xs', 'xs'],
33757 ['tall', 'xs', 'xs'],
33769 Roo.each(items, function(item, k){
33771 switch (item.size) {
33772 // these layouts take up a full box,
33783 boxes.push([item]);
33806 var filterPattern = function(box, length)
33814 var pattern = box.slice(0, length);
33818 Roo.each(pattern, function(i){
33819 format.push(i.size);
33822 Roo.each(standard, function(s){
33824 if(String(s) != String(format)){
33833 if(!match && length == 1){
33838 filterPattern(box, length - 1);
33842 queue.push(pattern);
33844 box = box.slice(length, box.length);
33846 filterPattern(box, 4);
33852 Roo.each(boxes, function(box, k){
33858 if(box.length == 1){
33863 filterPattern(box, 4);
33867 this._processVerticalLayoutQueue( queue, isInstant );
33871 // _verticalAlternativeLayoutItems : function( items , isInstant )
33873 // if ( !items || !items.length ) {
33877 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33881 _horizontalLayoutItems : function ( items , isInstant)
33883 if ( !items || !items.length || items.length < 3) {
33889 var eItems = items.slice(0, 3);
33891 items = items.slice(3, items.length);
33894 ['xs', 'xs', 'xs', 'wide'],
33895 ['xs', 'xs', 'wide'],
33896 ['xs', 'xs', 'sm'],
33897 ['xs', 'xs', 'xs'],
33903 ['sm', 'xs', 'xs'],
33907 ['wide', 'xs', 'xs', 'xs'],
33908 ['wide', 'xs', 'xs'],
33921 Roo.each(items, function(item, k){
33923 switch (item.size) {
33934 boxes.push([item]);
33958 var filterPattern = function(box, length)
33966 var pattern = box.slice(0, length);
33970 Roo.each(pattern, function(i){
33971 format.push(i.size);
33974 Roo.each(standard, function(s){
33976 if(String(s) != String(format)){
33985 if(!match && length == 1){
33990 filterPattern(box, length - 1);
33994 queue.push(pattern);
33996 box = box.slice(length, box.length);
33998 filterPattern(box, 4);
34004 Roo.each(boxes, function(box, k){
34010 if(box.length == 1){
34015 filterPattern(box, 4);
34022 var pos = this.el.getBox(true);
34026 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34028 var hit_end = false;
34030 Roo.each(queue, function(box){
34034 Roo.each(box, function(b){
34036 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34046 Roo.each(box, function(b){
34048 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34051 mx = Math.max(mx, b.x);
34055 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34059 Roo.each(box, function(b){
34061 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34075 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34078 /** Sets position of item in DOM
34079 * @param {Element} item
34080 * @param {Number} x - horizontal position
34081 * @param {Number} y - vertical position
34082 * @param {Boolean} isInstant - disables transitions
34084 _processVerticalLayoutQueue : function( queue, isInstant )
34086 var pos = this.el.getBox(true);
34091 for (var i = 0; i < this.cols; i++){
34095 Roo.each(queue, function(box, k){
34097 var col = k % this.cols;
34099 Roo.each(box, function(b,kk){
34101 b.el.position('absolute');
34103 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34104 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34106 if(b.size == 'md-left' || b.size == 'md-right'){
34107 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34108 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34111 b.el.setWidth(width);
34112 b.el.setHeight(height);
34114 b.el.select('iframe',true).setSize(width,height);
34118 for (var i = 0; i < this.cols; i++){
34120 if(maxY[i] < maxY[col]){
34125 col = Math.min(col, i);
34129 x = pos.x + col * (this.colWidth + this.padWidth);
34133 var positions = [];
34135 switch (box.length){
34137 positions = this.getVerticalOneBoxColPositions(x, y, box);
34140 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34143 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34146 positions = this.getVerticalFourBoxColPositions(x, y, box);
34152 Roo.each(box, function(b,kk){
34154 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34156 var sz = b.el.getSize();
34158 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34166 for (var i = 0; i < this.cols; i++){
34167 mY = Math.max(mY, maxY[i]);
34170 this.el.setHeight(mY - pos.y);
34174 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34176 // var pos = this.el.getBox(true);
34179 // var maxX = pos.right;
34181 // var maxHeight = 0;
34183 // Roo.each(items, function(item, k){
34187 // item.el.position('absolute');
34189 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34191 // item.el.setWidth(width);
34193 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34195 // item.el.setHeight(height);
34198 // item.el.setXY([x, y], isInstant ? false : true);
34200 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34203 // y = y + height + this.alternativePadWidth;
34205 // maxHeight = maxHeight + height + this.alternativePadWidth;
34209 // this.el.setHeight(maxHeight);
34213 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34215 var pos = this.el.getBox(true);
34220 var maxX = pos.right;
34222 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34224 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34226 Roo.each(queue, function(box, k){
34228 Roo.each(box, function(b, kk){
34230 b.el.position('absolute');
34232 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34233 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34235 if(b.size == 'md-left' || b.size == 'md-right'){
34236 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34237 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34240 b.el.setWidth(width);
34241 b.el.setHeight(height);
34249 var positions = [];
34251 switch (box.length){
34253 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34256 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34259 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34262 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34268 Roo.each(box, function(b,kk){
34270 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34272 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34280 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34282 Roo.each(eItems, function(b,k){
34284 b.size = (k == 0) ? 'sm' : 'xs';
34285 b.x = (k == 0) ? 2 : 1;
34286 b.y = (k == 0) ? 2 : 1;
34288 b.el.position('absolute');
34290 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34292 b.el.setWidth(width);
34294 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34296 b.el.setHeight(height);
34300 var positions = [];
34303 x : maxX - this.unitWidth * 2 - this.gutter,
34308 x : maxX - this.unitWidth,
34309 y : minY + (this.unitWidth + this.gutter) * 2
34313 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34317 Roo.each(eItems, function(b,k){
34319 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34325 getVerticalOneBoxColPositions : function(x, y, box)
34329 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34331 if(box[0].size == 'md-left'){
34335 if(box[0].size == 'md-right'){
34340 x : x + (this.unitWidth + this.gutter) * rand,
34347 getVerticalTwoBoxColPositions : function(x, y, box)
34351 if(box[0].size == 'xs'){
34355 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34359 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34373 x : x + (this.unitWidth + this.gutter) * 2,
34374 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34381 getVerticalThreeBoxColPositions : function(x, y, box)
34385 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34393 x : x + (this.unitWidth + this.gutter) * 1,
34398 x : x + (this.unitWidth + this.gutter) * 2,
34406 if(box[0].size == 'xs' && box[1].size == 'xs'){
34415 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34419 x : x + (this.unitWidth + this.gutter) * 1,
34433 x : x + (this.unitWidth + this.gutter) * 2,
34438 x : x + (this.unitWidth + this.gutter) * 2,
34439 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34446 getVerticalFourBoxColPositions : function(x, y, box)
34450 if(box[0].size == 'xs'){
34459 y : y + (this.unitHeight + this.gutter) * 1
34464 y : y + (this.unitHeight + this.gutter) * 2
34468 x : x + (this.unitWidth + this.gutter) * 1,
34482 x : x + (this.unitWidth + this.gutter) * 2,
34487 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34488 y : y + (this.unitHeight + this.gutter) * 1
34492 x : x + (this.unitWidth + this.gutter) * 2,
34493 y : y + (this.unitWidth + this.gutter) * 2
34500 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34504 if(box[0].size == 'md-left'){
34506 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34513 if(box[0].size == 'md-right'){
34515 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34516 y : minY + (this.unitWidth + this.gutter) * 1
34522 var rand = Math.floor(Math.random() * (4 - box[0].y));
34525 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34526 y : minY + (this.unitWidth + this.gutter) * rand
34533 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34537 if(box[0].size == 'xs'){
34540 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34545 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34546 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34554 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34559 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34560 y : minY + (this.unitWidth + this.gutter) * 2
34567 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34571 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34574 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34579 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34580 y : minY + (this.unitWidth + this.gutter) * 1
34584 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34585 y : minY + (this.unitWidth + this.gutter) * 2
34592 if(box[0].size == 'xs' && box[1].size == 'xs'){
34595 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34600 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34605 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34606 y : minY + (this.unitWidth + this.gutter) * 1
34614 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34619 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34620 y : minY + (this.unitWidth + this.gutter) * 2
34624 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34625 y : minY + (this.unitWidth + this.gutter) * 2
34632 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34636 if(box[0].size == 'xs'){
34639 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34644 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34649 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),
34654 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34655 y : minY + (this.unitWidth + this.gutter) * 1
34663 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34668 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34669 y : minY + (this.unitWidth + this.gutter) * 2
34673 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34674 y : minY + (this.unitWidth + this.gutter) * 2
34678 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),
34679 y : minY + (this.unitWidth + this.gutter) * 2
34687 * remove a Masonry Brick
34688 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34690 removeBrick : function(brick_id)
34696 for (var i = 0; i<this.bricks.length; i++) {
34697 if (this.bricks[i].id == brick_id) {
34698 this.bricks.splice(i,1);
34699 this.el.dom.removeChild(Roo.get(brick_id).dom);
34706 * adds a Masonry Brick
34707 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34709 addBrick : function(cfg)
34711 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34712 //this.register(cn);
34713 cn.parentId = this.id;
34714 cn.render(this.el);
34719 * register a Masonry Brick
34720 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34723 register : function(brick)
34725 this.bricks.push(brick);
34726 brick.masonryId = this.id;
34730 * clear all the Masonry Brick
34732 clearAll : function()
34735 //this.getChildContainer().dom.innerHTML = "";
34736 this.el.dom.innerHTML = '';
34739 getSelected : function()
34741 if (!this.selectedBrick) {
34745 return this.selectedBrick;
34749 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34753 * register a Masonry Layout
34754 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34757 register : function(layout)
34759 this.groups[layout.id] = layout;
34762 * fetch a Masonry Layout based on the masonry layout ID
34763 * @param {string} the masonry layout to add
34764 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34767 get: function(layout_id) {
34768 if (typeof(this.groups[layout_id]) == 'undefined') {
34771 return this.groups[layout_id] ;
34783 * http://masonry.desandro.com
34785 * The idea is to render all the bricks based on vertical width...
34787 * The original code extends 'outlayer' - we might need to use that....
34793 * @class Roo.bootstrap.LayoutMasonryAuto
34794 * @extends Roo.bootstrap.Component
34795 * Bootstrap Layout Masonry class
34798 * Create a new Element
34799 * @param {Object} config The config object
34802 Roo.bootstrap.LayoutMasonryAuto = function(config){
34803 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34806 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34809 * @cfg {Boolean} isFitWidth - resize the width..
34811 isFitWidth : false, // options..
34813 * @cfg {Boolean} isOriginLeft = left align?
34815 isOriginLeft : true,
34817 * @cfg {Boolean} isOriginTop = top align?
34819 isOriginTop : false,
34821 * @cfg {Boolean} isLayoutInstant = no animation?
34823 isLayoutInstant : false, // needed?
34825 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34827 isResizingContainer : true,
34829 * @cfg {Number} columnWidth width of the columns
34835 * @cfg {Number} maxCols maximum number of columns
34840 * @cfg {Number} padHeight padding below box..
34846 * @cfg {Boolean} isAutoInitial defalut true
34849 isAutoInitial : true,
34855 initialColumnWidth : 0,
34856 currentSize : null,
34858 colYs : null, // array.
34865 bricks: null, //CompositeElement
34866 cols : 0, // array?
34867 // element : null, // wrapped now this.el
34868 _isLayoutInited : null,
34871 getAutoCreate : function(){
34875 cls: 'blog-masonary-wrapper ' + this.cls,
34877 cls : 'mas-boxes masonary'
34884 getChildContainer: function( )
34886 if (this.boxesEl) {
34887 return this.boxesEl;
34890 this.boxesEl = this.el.select('.mas-boxes').first();
34892 return this.boxesEl;
34896 initEvents : function()
34900 if(this.isAutoInitial){
34901 Roo.log('hook children rendered');
34902 this.on('childrenrendered', function() {
34903 Roo.log('children rendered');
34910 initial : function()
34912 this.reloadItems();
34914 this.currentSize = this.el.getBox(true);
34916 /// was window resize... - let's see if this works..
34917 Roo.EventManager.onWindowResize(this.resize, this);
34919 if(!this.isAutoInitial){
34924 this.layout.defer(500,this);
34927 reloadItems: function()
34929 this.bricks = this.el.select('.masonry-brick', true);
34931 this.bricks.each(function(b) {
34932 //Roo.log(b.getSize());
34933 if (!b.attr('originalwidth')) {
34934 b.attr('originalwidth', b.getSize().width);
34939 Roo.log(this.bricks.elements.length);
34942 resize : function()
34945 var cs = this.el.getBox(true);
34947 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34948 Roo.log("no change in with or X");
34951 this.currentSize = cs;
34955 layout : function()
34958 this._resetLayout();
34959 //this._manageStamps();
34961 // don't animate first layout
34962 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34963 this.layoutItems( isInstant );
34965 // flag for initalized
34966 this._isLayoutInited = true;
34969 layoutItems : function( isInstant )
34971 //var items = this._getItemsForLayout( this.items );
34972 // original code supports filtering layout items.. we just ignore it..
34974 this._layoutItems( this.bricks , isInstant );
34976 this._postLayout();
34978 _layoutItems : function ( items , isInstant)
34980 //this.fireEvent( 'layout', this, items );
34983 if ( !items || !items.elements.length ) {
34984 // no items, emit event with empty array
34989 items.each(function(item) {
34990 Roo.log("layout item");
34992 // get x/y object from method
34993 var position = this._getItemLayoutPosition( item );
34995 position.item = item;
34996 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34997 queue.push( position );
35000 this._processLayoutQueue( queue );
35002 /** Sets position of item in DOM
35003 * @param {Element} item
35004 * @param {Number} x - horizontal position
35005 * @param {Number} y - vertical position
35006 * @param {Boolean} isInstant - disables transitions
35008 _processLayoutQueue : function( queue )
35010 for ( var i=0, len = queue.length; i < len; i++ ) {
35011 var obj = queue[i];
35012 obj.item.position('absolute');
35013 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35019 * Any logic you want to do after each layout,
35020 * i.e. size the container
35022 _postLayout : function()
35024 this.resizeContainer();
35027 resizeContainer : function()
35029 if ( !this.isResizingContainer ) {
35032 var size = this._getContainerSize();
35034 this.el.setSize(size.width,size.height);
35035 this.boxesEl.setSize(size.width,size.height);
35041 _resetLayout : function()
35043 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35044 this.colWidth = this.el.getWidth();
35045 //this.gutter = this.el.getWidth();
35047 this.measureColumns();
35053 this.colYs.push( 0 );
35059 measureColumns : function()
35061 this.getContainerWidth();
35062 // if columnWidth is 0, default to outerWidth of first item
35063 if ( !this.columnWidth ) {
35064 var firstItem = this.bricks.first();
35065 Roo.log(firstItem);
35066 this.columnWidth = this.containerWidth;
35067 if (firstItem && firstItem.attr('originalwidth') ) {
35068 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35070 // columnWidth fall back to item of first element
35071 Roo.log("set column width?");
35072 this.initialColumnWidth = this.columnWidth ;
35074 // if first elem has no width, default to size of container
35079 if (this.initialColumnWidth) {
35080 this.columnWidth = this.initialColumnWidth;
35085 // column width is fixed at the top - however if container width get's smaller we should
35088 // this bit calcs how man columns..
35090 var columnWidth = this.columnWidth += this.gutter;
35092 // calculate columns
35093 var containerWidth = this.containerWidth + this.gutter;
35095 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35096 // fix rounding errors, typically with gutters
35097 var excess = columnWidth - containerWidth % columnWidth;
35100 // if overshoot is less than a pixel, round up, otherwise floor it
35101 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35102 cols = Math[ mathMethod ]( cols );
35103 this.cols = Math.max( cols, 1 );
35104 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35106 // padding positioning..
35107 var totalColWidth = this.cols * this.columnWidth;
35108 var padavail = this.containerWidth - totalColWidth;
35109 // so for 2 columns - we need 3 'pads'
35111 var padNeeded = (1+this.cols) * this.padWidth;
35113 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35115 this.columnWidth += padExtra
35116 //this.padWidth = Math.floor(padavail / ( this.cols));
35118 // adjust colum width so that padding is fixed??
35120 // we have 3 columns ... total = width * 3
35121 // we have X left over... that should be used by
35123 //if (this.expandC) {
35131 getContainerWidth : function()
35133 /* // container is parent if fit width
35134 var container = this.isFitWidth ? this.element.parentNode : this.element;
35135 // check that this.size and size are there
35136 // IE8 triggers resize on body size change, so they might not be
35138 var size = getSize( container ); //FIXME
35139 this.containerWidth = size && size.innerWidth; //FIXME
35142 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35146 _getItemLayoutPosition : function( item ) // what is item?
35148 // we resize the item to our columnWidth..
35150 item.setWidth(this.columnWidth);
35151 item.autoBoxAdjust = false;
35153 var sz = item.getSize();
35155 // how many columns does this brick span
35156 var remainder = this.containerWidth % this.columnWidth;
35158 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35159 // round if off by 1 pixel, otherwise use ceil
35160 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35161 colSpan = Math.min( colSpan, this.cols );
35163 // normally this should be '1' as we dont' currently allow multi width columns..
35165 var colGroup = this._getColGroup( colSpan );
35166 // get the minimum Y value from the columns
35167 var minimumY = Math.min.apply( Math, colGroup );
35168 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35170 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35172 // position the brick
35174 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35175 y: this.currentSize.y + minimumY + this.padHeight
35179 // apply setHeight to necessary columns
35180 var setHeight = minimumY + sz.height + this.padHeight;
35181 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35183 var setSpan = this.cols + 1 - colGroup.length;
35184 for ( var i = 0; i < setSpan; i++ ) {
35185 this.colYs[ shortColIndex + i ] = setHeight ;
35192 * @param {Number} colSpan - number of columns the element spans
35193 * @returns {Array} colGroup
35195 _getColGroup : function( colSpan )
35197 if ( colSpan < 2 ) {
35198 // if brick spans only one column, use all the column Ys
35203 // how many different places could this brick fit horizontally
35204 var groupCount = this.cols + 1 - colSpan;
35205 // for each group potential horizontal position
35206 for ( var i = 0; i < groupCount; i++ ) {
35207 // make an array of colY values for that one group
35208 var groupColYs = this.colYs.slice( i, i + colSpan );
35209 // and get the max value of the array
35210 colGroup[i] = Math.max.apply( Math, groupColYs );
35215 _manageStamp : function( stamp )
35217 var stampSize = stamp.getSize();
35218 var offset = stamp.getBox();
35219 // get the columns that this stamp affects
35220 var firstX = this.isOriginLeft ? offset.x : offset.right;
35221 var lastX = firstX + stampSize.width;
35222 var firstCol = Math.floor( firstX / this.columnWidth );
35223 firstCol = Math.max( 0, firstCol );
35225 var lastCol = Math.floor( lastX / this.columnWidth );
35226 // lastCol should not go over if multiple of columnWidth #425
35227 lastCol -= lastX % this.columnWidth ? 0 : 1;
35228 lastCol = Math.min( this.cols - 1, lastCol );
35230 // set colYs to bottom of the stamp
35231 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35234 for ( var i = firstCol; i <= lastCol; i++ ) {
35235 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35240 _getContainerSize : function()
35242 this.maxY = Math.max.apply( Math, this.colYs );
35247 if ( this.isFitWidth ) {
35248 size.width = this._getContainerFitWidth();
35254 _getContainerFitWidth : function()
35256 var unusedCols = 0;
35257 // count unused columns
35260 if ( this.colYs[i] !== 0 ) {
35265 // fit container to columns that have been used
35266 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35269 needsResizeLayout : function()
35271 var previousWidth = this.containerWidth;
35272 this.getContainerWidth();
35273 return previousWidth !== this.containerWidth;
35288 * @class Roo.bootstrap.MasonryBrick
35289 * @extends Roo.bootstrap.Component
35290 * Bootstrap MasonryBrick class
35293 * Create a new MasonryBrick
35294 * @param {Object} config The config object
35297 Roo.bootstrap.MasonryBrick = function(config){
35299 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35301 Roo.bootstrap.MasonryBrick.register(this);
35307 * When a MasonryBrick is clcik
35308 * @param {Roo.bootstrap.MasonryBrick} this
35309 * @param {Roo.EventObject} e
35315 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35318 * @cfg {String} title
35322 * @cfg {String} html
35326 * @cfg {String} bgimage
35330 * @cfg {String} videourl
35334 * @cfg {String} cls
35338 * @cfg {String} href
35342 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35347 * @cfg {String} placetitle (center|bottom)
35352 * @cfg {Boolean} isFitContainer defalut true
35354 isFitContainer : true,
35357 * @cfg {Boolean} preventDefault defalut false
35359 preventDefault : false,
35362 * @cfg {Boolean} inverse defalut false
35364 maskInverse : false,
35366 getAutoCreate : function()
35368 if(!this.isFitContainer){
35369 return this.getSplitAutoCreate();
35372 var cls = 'masonry-brick masonry-brick-full';
35374 if(this.href.length){
35375 cls += ' masonry-brick-link';
35378 if(this.bgimage.length){
35379 cls += ' masonry-brick-image';
35382 if(this.maskInverse){
35383 cls += ' mask-inverse';
35386 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35387 cls += ' enable-mask';
35391 cls += ' masonry-' + this.size + '-brick';
35394 if(this.placetitle.length){
35396 switch (this.placetitle) {
35398 cls += ' masonry-center-title';
35401 cls += ' masonry-bottom-title';
35408 if(!this.html.length && !this.bgimage.length){
35409 cls += ' masonry-center-title';
35412 if(!this.html.length && this.bgimage.length){
35413 cls += ' masonry-bottom-title';
35418 cls += ' ' + this.cls;
35422 tag: (this.href.length) ? 'a' : 'div',
35427 cls: 'masonry-brick-mask'
35431 cls: 'masonry-brick-paragraph',
35437 if(this.href.length){
35438 cfg.href = this.href;
35441 var cn = cfg.cn[1].cn;
35443 if(this.title.length){
35446 cls: 'masonry-brick-title',
35451 if(this.html.length){
35454 cls: 'masonry-brick-text',
35459 if (!this.title.length && !this.html.length) {
35460 cfg.cn[1].cls += ' hide';
35463 if(this.bgimage.length){
35466 cls: 'masonry-brick-image-view',
35471 if(this.videourl.length){
35472 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35473 // youtube support only?
35476 cls: 'masonry-brick-image-view',
35479 allowfullscreen : true
35487 getSplitAutoCreate : function()
35489 var cls = 'masonry-brick masonry-brick-split';
35491 if(this.href.length){
35492 cls += ' masonry-brick-link';
35495 if(this.bgimage.length){
35496 cls += ' masonry-brick-image';
35500 cls += ' masonry-' + this.size + '-brick';
35503 switch (this.placetitle) {
35505 cls += ' masonry-center-title';
35508 cls += ' masonry-bottom-title';
35511 if(!this.bgimage.length){
35512 cls += ' masonry-center-title';
35515 if(this.bgimage.length){
35516 cls += ' masonry-bottom-title';
35522 cls += ' ' + this.cls;
35526 tag: (this.href.length) ? 'a' : 'div',
35531 cls: 'masonry-brick-split-head',
35535 cls: 'masonry-brick-paragraph',
35542 cls: 'masonry-brick-split-body',
35548 if(this.href.length){
35549 cfg.href = this.href;
35552 if(this.title.length){
35553 cfg.cn[0].cn[0].cn.push({
35555 cls: 'masonry-brick-title',
35560 if(this.html.length){
35561 cfg.cn[1].cn.push({
35563 cls: 'masonry-brick-text',
35568 if(this.bgimage.length){
35569 cfg.cn[0].cn.push({
35571 cls: 'masonry-brick-image-view',
35576 if(this.videourl.length){
35577 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35578 // youtube support only?
35579 cfg.cn[0].cn.cn.push({
35581 cls: 'masonry-brick-image-view',
35584 allowfullscreen : true
35591 initEvents: function()
35593 switch (this.size) {
35626 this.el.on('touchstart', this.onTouchStart, this);
35627 this.el.on('touchmove', this.onTouchMove, this);
35628 this.el.on('touchend', this.onTouchEnd, this);
35629 this.el.on('contextmenu', this.onContextMenu, this);
35631 this.el.on('mouseenter' ,this.enter, this);
35632 this.el.on('mouseleave', this.leave, this);
35633 this.el.on('click', this.onClick, this);
35636 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35637 this.parent().bricks.push(this);
35642 onClick: function(e, el)
35644 var time = this.endTimer - this.startTimer;
35645 // Roo.log(e.preventDefault());
35648 e.preventDefault();
35653 if(!this.preventDefault){
35657 e.preventDefault();
35659 if (this.activeClass != '') {
35660 this.selectBrick();
35663 this.fireEvent('click', this, e);
35666 enter: function(e, el)
35668 e.preventDefault();
35670 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35674 if(this.bgimage.length && this.html.length){
35675 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35679 leave: function(e, el)
35681 e.preventDefault();
35683 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35687 if(this.bgimage.length && this.html.length){
35688 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35692 onTouchStart: function(e, el)
35694 // e.preventDefault();
35696 this.touchmoved = false;
35698 if(!this.isFitContainer){
35702 if(!this.bgimage.length || !this.html.length){
35706 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35708 this.timer = new Date().getTime();
35712 onTouchMove: function(e, el)
35714 this.touchmoved = true;
35717 onContextMenu : function(e,el)
35719 e.preventDefault();
35720 e.stopPropagation();
35724 onTouchEnd: function(e, el)
35726 // e.preventDefault();
35728 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35735 if(!this.bgimage.length || !this.html.length){
35737 if(this.href.length){
35738 window.location.href = this.href;
35744 if(!this.isFitContainer){
35748 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35750 window.location.href = this.href;
35753 //selection on single brick only
35754 selectBrick : function() {
35756 if (!this.parentId) {
35760 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35761 var index = m.selectedBrick.indexOf(this.id);
35764 m.selectedBrick.splice(index,1);
35765 this.el.removeClass(this.activeClass);
35769 for(var i = 0; i < m.selectedBrick.length; i++) {
35770 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35771 b.el.removeClass(b.activeClass);
35774 m.selectedBrick = [];
35776 m.selectedBrick.push(this.id);
35777 this.el.addClass(this.activeClass);
35781 isSelected : function(){
35782 return this.el.hasClass(this.activeClass);
35787 Roo.apply(Roo.bootstrap.MasonryBrick, {
35790 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35792 * register a Masonry Brick
35793 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35796 register : function(brick)
35798 //this.groups[brick.id] = brick;
35799 this.groups.add(brick.id, brick);
35802 * fetch a masonry brick based on the masonry brick ID
35803 * @param {string} the masonry brick to add
35804 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35807 get: function(brick_id)
35809 // if (typeof(this.groups[brick_id]) == 'undefined') {
35812 // return this.groups[brick_id] ;
35814 if(this.groups.key(brick_id)) {
35815 return this.groups.key(brick_id);
35833 * @class Roo.bootstrap.Brick
35834 * @extends Roo.bootstrap.Component
35835 * Bootstrap Brick class
35838 * Create a new Brick
35839 * @param {Object} config The config object
35842 Roo.bootstrap.Brick = function(config){
35843 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35849 * When a Brick is click
35850 * @param {Roo.bootstrap.Brick} this
35851 * @param {Roo.EventObject} e
35857 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35860 * @cfg {String} title
35864 * @cfg {String} html
35868 * @cfg {String} bgimage
35872 * @cfg {String} cls
35876 * @cfg {String} href
35880 * @cfg {String} video
35884 * @cfg {Boolean} square
35888 getAutoCreate : function()
35890 var cls = 'roo-brick';
35892 if(this.href.length){
35893 cls += ' roo-brick-link';
35896 if(this.bgimage.length){
35897 cls += ' roo-brick-image';
35900 if(!this.html.length && !this.bgimage.length){
35901 cls += ' roo-brick-center-title';
35904 if(!this.html.length && this.bgimage.length){
35905 cls += ' roo-brick-bottom-title';
35909 cls += ' ' + this.cls;
35913 tag: (this.href.length) ? 'a' : 'div',
35918 cls: 'roo-brick-paragraph',
35924 if(this.href.length){
35925 cfg.href = this.href;
35928 var cn = cfg.cn[0].cn;
35930 if(this.title.length){
35933 cls: 'roo-brick-title',
35938 if(this.html.length){
35941 cls: 'roo-brick-text',
35948 if(this.bgimage.length){
35951 cls: 'roo-brick-image-view',
35959 initEvents: function()
35961 if(this.title.length || this.html.length){
35962 this.el.on('mouseenter' ,this.enter, this);
35963 this.el.on('mouseleave', this.leave, this);
35966 Roo.EventManager.onWindowResize(this.resize, this);
35968 if(this.bgimage.length){
35969 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35970 this.imageEl.on('load', this.onImageLoad, this);
35977 onImageLoad : function()
35982 resize : function()
35984 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35986 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35988 if(this.bgimage.length){
35989 var image = this.el.select('.roo-brick-image-view', true).first();
35991 image.setWidth(paragraph.getWidth());
35994 image.setHeight(paragraph.getWidth());
35997 this.el.setHeight(image.getHeight());
35998 paragraph.setHeight(image.getHeight());
36004 enter: function(e, el)
36006 e.preventDefault();
36008 if(this.bgimage.length){
36009 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36010 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36014 leave: function(e, el)
36016 e.preventDefault();
36018 if(this.bgimage.length){
36019 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36020 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36035 * @class Roo.bootstrap.NumberField
36036 * @extends Roo.bootstrap.Input
36037 * Bootstrap NumberField class
36043 * Create a new NumberField
36044 * @param {Object} config The config object
36047 Roo.bootstrap.NumberField = function(config){
36048 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36051 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36054 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36056 allowDecimals : true,
36058 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36060 decimalSeparator : ".",
36062 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36064 decimalPrecision : 2,
36066 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36068 allowNegative : true,
36071 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36075 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36077 minValue : Number.NEGATIVE_INFINITY,
36079 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36081 maxValue : Number.MAX_VALUE,
36083 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36085 minText : "The minimum value for this field is {0}",
36087 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36089 maxText : "The maximum value for this field is {0}",
36091 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36092 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36094 nanText : "{0} is not a valid number",
36096 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36098 thousandsDelimiter : false,
36100 * @cfg {String} valueAlign alignment of value
36102 valueAlign : "left",
36104 getAutoCreate : function()
36106 var hiddenInput = {
36110 cls: 'hidden-number-input'
36114 hiddenInput.name = this.name;
36119 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36121 this.name = hiddenInput.name;
36123 if(cfg.cn.length > 0) {
36124 cfg.cn.push(hiddenInput);
36131 initEvents : function()
36133 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36135 var allowed = "0123456789";
36137 if(this.allowDecimals){
36138 allowed += this.decimalSeparator;
36141 if(this.allowNegative){
36145 if(this.thousandsDelimiter) {
36149 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36151 var keyPress = function(e){
36153 var k = e.getKey();
36155 var c = e.getCharCode();
36158 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36159 allowed.indexOf(String.fromCharCode(c)) === -1
36165 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36169 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36174 this.el.on("keypress", keyPress, this);
36177 validateValue : function(value)
36180 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36184 var num = this.parseValue(value);
36187 this.markInvalid(String.format(this.nanText, value));
36191 if(num < this.minValue){
36192 this.markInvalid(String.format(this.minText, this.minValue));
36196 if(num > this.maxValue){
36197 this.markInvalid(String.format(this.maxText, this.maxValue));
36204 getValue : function()
36206 var v = this.hiddenEl().getValue();
36208 return this.fixPrecision(this.parseValue(v));
36211 parseValue : function(value)
36213 if(this.thousandsDelimiter) {
36215 r = new RegExp(",", "g");
36216 value = value.replace(r, "");
36219 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36220 return isNaN(value) ? '' : value;
36223 fixPrecision : function(value)
36225 if(this.thousandsDelimiter) {
36227 r = new RegExp(",", "g");
36228 value = value.replace(r, "");
36231 var nan = isNaN(value);
36233 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36234 return nan ? '' : value;
36236 return parseFloat(value).toFixed(this.decimalPrecision);
36239 setValue : function(v)
36241 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36247 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36249 this.inputEl().dom.value = (v == '') ? '' :
36250 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36252 if(!this.allowZero && v === '0') {
36253 this.hiddenEl().dom.value = '';
36254 this.inputEl().dom.value = '';
36261 decimalPrecisionFcn : function(v)
36263 return Math.floor(v);
36266 beforeBlur : function()
36268 var v = this.parseValue(this.getRawValue());
36270 if(v || v === 0 || v === ''){
36275 hiddenEl : function()
36277 return this.el.select('input.hidden-number-input',true).first();
36289 * @class Roo.bootstrap.DocumentSlider
36290 * @extends Roo.bootstrap.Component
36291 * Bootstrap DocumentSlider class
36294 * Create a new DocumentViewer
36295 * @param {Object} config The config object
36298 Roo.bootstrap.DocumentSlider = function(config){
36299 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36306 * Fire after initEvent
36307 * @param {Roo.bootstrap.DocumentSlider} this
36312 * Fire after update
36313 * @param {Roo.bootstrap.DocumentSlider} this
36319 * @param {Roo.bootstrap.DocumentSlider} this
36325 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36331 getAutoCreate : function()
36335 cls : 'roo-document-slider',
36339 cls : 'roo-document-slider-header',
36343 cls : 'roo-document-slider-header-title'
36349 cls : 'roo-document-slider-body',
36353 cls : 'roo-document-slider-prev',
36357 cls : 'fa fa-chevron-left'
36363 cls : 'roo-document-slider-thumb',
36367 cls : 'roo-document-slider-image'
36373 cls : 'roo-document-slider-next',
36377 cls : 'fa fa-chevron-right'
36389 initEvents : function()
36391 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36392 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36394 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36395 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36397 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36398 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36400 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36401 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36403 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36404 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36406 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36407 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36409 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36410 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36412 this.thumbEl.on('click', this.onClick, this);
36414 this.prevIndicator.on('click', this.prev, this);
36416 this.nextIndicator.on('click', this.next, this);
36420 initial : function()
36422 if(this.files.length){
36423 this.indicator = 1;
36427 this.fireEvent('initial', this);
36430 update : function()
36432 this.imageEl.attr('src', this.files[this.indicator - 1]);
36434 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36436 this.prevIndicator.show();
36438 if(this.indicator == 1){
36439 this.prevIndicator.hide();
36442 this.nextIndicator.show();
36444 if(this.indicator == this.files.length){
36445 this.nextIndicator.hide();
36448 this.thumbEl.scrollTo('top');
36450 this.fireEvent('update', this);
36453 onClick : function(e)
36455 e.preventDefault();
36457 this.fireEvent('click', this);
36462 e.preventDefault();
36464 this.indicator = Math.max(1, this.indicator - 1);
36471 e.preventDefault();
36473 this.indicator = Math.min(this.files.length, this.indicator + 1);
36487 * @class Roo.bootstrap.RadioSet
36488 * @extends Roo.bootstrap.Input
36489 * Bootstrap RadioSet class
36490 * @cfg {String} indicatorpos (left|right) default left
36491 * @cfg {Boolean} inline (true|false) inline the element (default true)
36492 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36494 * Create a new RadioSet
36495 * @param {Object} config The config object
36498 Roo.bootstrap.RadioSet = function(config){
36500 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36504 Roo.bootstrap.RadioSet.register(this);
36509 * Fires when the element is checked or unchecked.
36510 * @param {Roo.bootstrap.RadioSet} this This radio
36511 * @param {Roo.bootstrap.Radio} item The checked item
36516 * Fires when the element is click.
36517 * @param {Roo.bootstrap.RadioSet} this This radio set
36518 * @param {Roo.bootstrap.Radio} item The checked item
36519 * @param {Roo.EventObject} e The event object
36526 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36534 indicatorpos : 'left',
36536 getAutoCreate : function()
36540 cls : 'roo-radio-set-label',
36544 html : this.fieldLabel
36548 if (Roo.bootstrap.version == 3) {
36551 if(this.indicatorpos == 'left'){
36554 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36555 tooltip : 'This field is required'
36560 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36561 tooltip : 'This field is required'
36567 cls : 'roo-radio-set-items'
36570 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36572 if (align === 'left' && this.fieldLabel.length) {
36575 cls : "roo-radio-set-right",
36581 if(this.labelWidth > 12){
36582 label.style = "width: " + this.labelWidth + 'px';
36585 if(this.labelWidth < 13 && this.labelmd == 0){
36586 this.labelmd = this.labelWidth;
36589 if(this.labellg > 0){
36590 label.cls += ' col-lg-' + this.labellg;
36591 items.cls += ' col-lg-' + (12 - this.labellg);
36594 if(this.labelmd > 0){
36595 label.cls += ' col-md-' + this.labelmd;
36596 items.cls += ' col-md-' + (12 - this.labelmd);
36599 if(this.labelsm > 0){
36600 label.cls += ' col-sm-' + this.labelsm;
36601 items.cls += ' col-sm-' + (12 - this.labelsm);
36604 if(this.labelxs > 0){
36605 label.cls += ' col-xs-' + this.labelxs;
36606 items.cls += ' col-xs-' + (12 - this.labelxs);
36612 cls : 'roo-radio-set',
36616 cls : 'roo-radio-set-input',
36619 value : this.value ? this.value : ''
36626 if(this.weight.length){
36627 cfg.cls += ' roo-radio-' + this.weight;
36631 cfg.cls += ' roo-radio-set-inline';
36635 ['xs','sm','md','lg'].map(function(size){
36636 if (settings[size]) {
36637 cfg.cls += ' col-' + size + '-' + settings[size];
36645 initEvents : function()
36647 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36648 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36650 if(!this.fieldLabel.length){
36651 this.labelEl.hide();
36654 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36655 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36657 this.indicator = this.indicatorEl();
36659 if(this.indicator){
36660 this.indicator.addClass('invisible');
36663 this.originalValue = this.getValue();
36667 inputEl: function ()
36669 return this.el.select('.roo-radio-set-input', true).first();
36672 getChildContainer : function()
36674 return this.itemsEl;
36677 register : function(item)
36679 this.radioes.push(item);
36683 validate : function()
36685 if(this.getVisibilityEl().hasClass('hidden')){
36691 Roo.each(this.radioes, function(i){
36700 if(this.allowBlank) {
36704 if(this.disabled || valid){
36709 this.markInvalid();
36714 markValid : function()
36716 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36717 this.indicatorEl().removeClass('visible');
36718 this.indicatorEl().addClass('invisible');
36722 if (Roo.bootstrap.version == 3) {
36723 this.el.removeClass([this.invalidClass, this.validClass]);
36724 this.el.addClass(this.validClass);
36726 this.el.removeClass(['is-invalid','is-valid']);
36727 this.el.addClass(['is-valid']);
36729 this.fireEvent('valid', this);
36732 markInvalid : function(msg)
36734 if(this.allowBlank || this.disabled){
36738 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36739 this.indicatorEl().removeClass('invisible');
36740 this.indicatorEl().addClass('visible');
36742 if (Roo.bootstrap.version == 3) {
36743 this.el.removeClass([this.invalidClass, this.validClass]);
36744 this.el.addClass(this.invalidClass);
36746 this.el.removeClass(['is-invalid','is-valid']);
36747 this.el.addClass(['is-invalid']);
36750 this.fireEvent('invalid', this, msg);
36754 setValue : function(v, suppressEvent)
36756 if(this.value === v){
36763 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36766 Roo.each(this.radioes, function(i){
36768 i.el.removeClass('checked');
36771 Roo.each(this.radioes, function(i){
36773 if(i.value === v || i.value.toString() === v.toString()){
36775 i.el.addClass('checked');
36777 if(suppressEvent !== true){
36778 this.fireEvent('check', this, i);
36789 clearInvalid : function(){
36791 if(!this.el || this.preventMark){
36795 this.el.removeClass([this.invalidClass]);
36797 this.fireEvent('valid', this);
36802 Roo.apply(Roo.bootstrap.RadioSet, {
36806 register : function(set)
36808 this.groups[set.name] = set;
36811 get: function(name)
36813 if (typeof(this.groups[name]) == 'undefined') {
36817 return this.groups[name] ;
36823 * Ext JS Library 1.1.1
36824 * Copyright(c) 2006-2007, Ext JS, LLC.
36826 * Originally Released Under LGPL - original licence link has changed is not relivant.
36829 * <script type="text/javascript">
36834 * @class Roo.bootstrap.SplitBar
36835 * @extends Roo.util.Observable
36836 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36840 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36841 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36842 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36843 split.minSize = 100;
36844 split.maxSize = 600;
36845 split.animate = true;
36846 split.on('moved', splitterMoved);
36849 * Create a new SplitBar
36850 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36851 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36852 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36853 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36854 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36855 position of the SplitBar).
36857 Roo.bootstrap.SplitBar = function(cfg){
36862 // dragElement : elm
36863 // resizingElement: el,
36865 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36866 // placement : Roo.bootstrap.SplitBar.LEFT ,
36867 // existingProxy ???
36870 this.el = Roo.get(cfg.dragElement, true);
36871 this.el.dom.unselectable = "on";
36873 this.resizingEl = Roo.get(cfg.resizingElement, true);
36877 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36878 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36881 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36884 * The minimum size of the resizing element. (Defaults to 0)
36890 * The maximum size of the resizing element. (Defaults to 2000)
36893 this.maxSize = 2000;
36896 * Whether to animate the transition to the new size
36899 this.animate = false;
36902 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36905 this.useShim = false;
36910 if(!cfg.existingProxy){
36912 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36914 this.proxy = Roo.get(cfg.existingProxy).dom;
36917 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36920 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36923 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36926 this.dragSpecs = {};
36929 * @private The adapter to use to positon and resize elements
36931 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36932 this.adapter.init(this);
36934 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36936 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36937 this.el.addClass("roo-splitbar-h");
36940 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36941 this.el.addClass("roo-splitbar-v");
36947 * Fires when the splitter is moved (alias for {@link #event-moved})
36948 * @param {Roo.bootstrap.SplitBar} this
36949 * @param {Number} newSize the new width or height
36954 * Fires when the splitter is moved
36955 * @param {Roo.bootstrap.SplitBar} this
36956 * @param {Number} newSize the new width or height
36960 * @event beforeresize
36961 * Fires before the splitter is dragged
36962 * @param {Roo.bootstrap.SplitBar} this
36964 "beforeresize" : true,
36966 "beforeapply" : true
36969 Roo.util.Observable.call(this);
36972 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36973 onStartProxyDrag : function(x, y){
36974 this.fireEvent("beforeresize", this);
36976 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36978 o.enableDisplayMode("block");
36979 // all splitbars share the same overlay
36980 Roo.bootstrap.SplitBar.prototype.overlay = o;
36982 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36983 this.overlay.show();
36984 Roo.get(this.proxy).setDisplayed("block");
36985 var size = this.adapter.getElementSize(this);
36986 this.activeMinSize = this.getMinimumSize();;
36987 this.activeMaxSize = this.getMaximumSize();;
36988 var c1 = size - this.activeMinSize;
36989 var c2 = Math.max(this.activeMaxSize - size, 0);
36990 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36991 this.dd.resetConstraints();
36992 this.dd.setXConstraint(
36993 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36994 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36996 this.dd.setYConstraint(0, 0);
36998 this.dd.resetConstraints();
36999 this.dd.setXConstraint(0, 0);
37000 this.dd.setYConstraint(
37001 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37002 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37005 this.dragSpecs.startSize = size;
37006 this.dragSpecs.startPoint = [x, y];
37007 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37011 * @private Called after the drag operation by the DDProxy
37013 onEndProxyDrag : function(e){
37014 Roo.get(this.proxy).setDisplayed(false);
37015 var endPoint = Roo.lib.Event.getXY(e);
37017 this.overlay.hide();
37020 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37021 newSize = this.dragSpecs.startSize +
37022 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37023 endPoint[0] - this.dragSpecs.startPoint[0] :
37024 this.dragSpecs.startPoint[0] - endPoint[0]
37027 newSize = this.dragSpecs.startSize +
37028 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37029 endPoint[1] - this.dragSpecs.startPoint[1] :
37030 this.dragSpecs.startPoint[1] - endPoint[1]
37033 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37034 if(newSize != this.dragSpecs.startSize){
37035 if(this.fireEvent('beforeapply', this, newSize) !== false){
37036 this.adapter.setElementSize(this, newSize);
37037 this.fireEvent("moved", this, newSize);
37038 this.fireEvent("resize", this, newSize);
37044 * Get the adapter this SplitBar uses
37045 * @return The adapter object
37047 getAdapter : function(){
37048 return this.adapter;
37052 * Set the adapter this SplitBar uses
37053 * @param {Object} adapter A SplitBar adapter object
37055 setAdapter : function(adapter){
37056 this.adapter = adapter;
37057 this.adapter.init(this);
37061 * Gets the minimum size for the resizing element
37062 * @return {Number} The minimum size
37064 getMinimumSize : function(){
37065 return this.minSize;
37069 * Sets the minimum size for the resizing element
37070 * @param {Number} minSize The minimum size
37072 setMinimumSize : function(minSize){
37073 this.minSize = minSize;
37077 * Gets the maximum size for the resizing element
37078 * @return {Number} The maximum size
37080 getMaximumSize : function(){
37081 return this.maxSize;
37085 * Sets the maximum size for the resizing element
37086 * @param {Number} maxSize The maximum size
37088 setMaximumSize : function(maxSize){
37089 this.maxSize = maxSize;
37093 * Sets the initialize size for the resizing element
37094 * @param {Number} size The initial size
37096 setCurrentSize : function(size){
37097 var oldAnimate = this.animate;
37098 this.animate = false;
37099 this.adapter.setElementSize(this, size);
37100 this.animate = oldAnimate;
37104 * Destroy this splitbar.
37105 * @param {Boolean} removeEl True to remove the element
37107 destroy : function(removeEl){
37109 this.shim.remove();
37112 this.proxy.parentNode.removeChild(this.proxy);
37120 * @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.
37122 Roo.bootstrap.SplitBar.createProxy = function(dir){
37123 var proxy = new Roo.Element(document.createElement("div"));
37124 proxy.unselectable();
37125 var cls = 'roo-splitbar-proxy';
37126 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37127 document.body.appendChild(proxy.dom);
37132 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37133 * Default Adapter. It assumes the splitter and resizing element are not positioned
37134 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37136 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37139 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37140 // do nothing for now
37141 init : function(s){
37145 * Called before drag operations to get the current size of the resizing element.
37146 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37148 getElementSize : function(s){
37149 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37150 return s.resizingEl.getWidth();
37152 return s.resizingEl.getHeight();
37157 * Called after drag operations to set the size of the resizing element.
37158 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37159 * @param {Number} newSize The new size to set
37160 * @param {Function} onComplete A function to be invoked when resizing is complete
37162 setElementSize : function(s, newSize, onComplete){
37163 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37165 s.resizingEl.setWidth(newSize);
37167 onComplete(s, newSize);
37170 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37175 s.resizingEl.setHeight(newSize);
37177 onComplete(s, newSize);
37180 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37187 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37188 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37189 * Adapter that moves the splitter element to align with the resized sizing element.
37190 * Used with an absolute positioned SplitBar.
37191 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37192 * document.body, make sure you assign an id to the body element.
37194 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37195 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37196 this.container = Roo.get(container);
37199 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37200 init : function(s){
37201 this.basic.init(s);
37204 getElementSize : function(s){
37205 return this.basic.getElementSize(s);
37208 setElementSize : function(s, newSize, onComplete){
37209 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37212 moveSplitter : function(s){
37213 var yes = Roo.bootstrap.SplitBar;
37214 switch(s.placement){
37216 s.el.setX(s.resizingEl.getRight());
37219 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37222 s.el.setY(s.resizingEl.getBottom());
37225 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37232 * Orientation constant - Create a vertical SplitBar
37236 Roo.bootstrap.SplitBar.VERTICAL = 1;
37239 * Orientation constant - Create a horizontal SplitBar
37243 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37246 * Placement constant - The resizing element is to the left of the splitter element
37250 Roo.bootstrap.SplitBar.LEFT = 1;
37253 * Placement constant - The resizing element is to the right of the splitter element
37257 Roo.bootstrap.SplitBar.RIGHT = 2;
37260 * Placement constant - The resizing element is positioned above the splitter element
37264 Roo.bootstrap.SplitBar.TOP = 3;
37267 * Placement constant - The resizing element is positioned under splitter element
37271 Roo.bootstrap.SplitBar.BOTTOM = 4;
37272 Roo.namespace("Roo.bootstrap.layout");/*
37274 * Ext JS Library 1.1.1
37275 * Copyright(c) 2006-2007, Ext JS, LLC.
37277 * Originally Released Under LGPL - original licence link has changed is not relivant.
37280 * <script type="text/javascript">
37284 * @class Roo.bootstrap.layout.Manager
37285 * @extends Roo.bootstrap.Component
37286 * Base class for layout managers.
37288 Roo.bootstrap.layout.Manager = function(config)
37290 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37296 /** false to disable window resize monitoring @type Boolean */
37297 this.monitorWindowResize = true;
37302 * Fires when a layout is performed.
37303 * @param {Roo.LayoutManager} this
37307 * @event regionresized
37308 * Fires when the user resizes a region.
37309 * @param {Roo.LayoutRegion} region The resized region
37310 * @param {Number} newSize The new size (width for east/west, height for north/south)
37312 "regionresized" : true,
37314 * @event regioncollapsed
37315 * Fires when a region is collapsed.
37316 * @param {Roo.LayoutRegion} region The collapsed region
37318 "regioncollapsed" : true,
37320 * @event regionexpanded
37321 * Fires when a region is expanded.
37322 * @param {Roo.LayoutRegion} region The expanded region
37324 "regionexpanded" : true
37326 this.updating = false;
37329 this.el = Roo.get(config.el);
37335 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37340 monitorWindowResize : true,
37346 onRender : function(ct, position)
37349 this.el = Roo.get(ct);
37352 //this.fireEvent('render',this);
37356 initEvents: function()
37360 // ie scrollbar fix
37361 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37362 document.body.scroll = "no";
37363 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37364 this.el.position('relative');
37366 this.id = this.el.id;
37367 this.el.addClass("roo-layout-container");
37368 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37369 if(this.el.dom != document.body ) {
37370 this.el.on('resize', this.layout,this);
37371 this.el.on('show', this.layout,this);
37377 * Returns true if this layout is currently being updated
37378 * @return {Boolean}
37380 isUpdating : function(){
37381 return this.updating;
37385 * Suspend the LayoutManager from doing auto-layouts while
37386 * making multiple add or remove calls
37388 beginUpdate : function(){
37389 this.updating = true;
37393 * Restore auto-layouts and optionally disable the manager from performing a layout
37394 * @param {Boolean} noLayout true to disable a layout update
37396 endUpdate : function(noLayout){
37397 this.updating = false;
37403 layout: function(){
37407 onRegionResized : function(region, newSize){
37408 this.fireEvent("regionresized", region, newSize);
37412 onRegionCollapsed : function(region){
37413 this.fireEvent("regioncollapsed", region);
37416 onRegionExpanded : function(region){
37417 this.fireEvent("regionexpanded", region);
37421 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37422 * performs box-model adjustments.
37423 * @return {Object} The size as an object {width: (the width), height: (the height)}
37425 getViewSize : function()
37428 if(this.el.dom != document.body){
37429 size = this.el.getSize();
37431 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37433 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37434 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37439 * Returns the Element this layout is bound to.
37440 * @return {Roo.Element}
37442 getEl : function(){
37447 * Returns the specified region.
37448 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37449 * @return {Roo.LayoutRegion}
37451 getRegion : function(target){
37452 return this.regions[target.toLowerCase()];
37455 onWindowResize : function(){
37456 if(this.monitorWindowResize){
37463 * Ext JS Library 1.1.1
37464 * Copyright(c) 2006-2007, Ext JS, LLC.
37466 * Originally Released Under LGPL - original licence link has changed is not relivant.
37469 * <script type="text/javascript">
37472 * @class Roo.bootstrap.layout.Border
37473 * @extends Roo.bootstrap.layout.Manager
37474 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37475 * please see: examples/bootstrap/nested.html<br><br>
37477 <b>The container the layout is rendered into can be either the body element or any other element.
37478 If it is not the body element, the container needs to either be an absolute positioned element,
37479 or you will need to add "position:relative" to the css of the container. You will also need to specify
37480 the container size if it is not the body element.</b>
37483 * Create a new Border
37484 * @param {Object} config Configuration options
37486 Roo.bootstrap.layout.Border = function(config){
37487 config = config || {};
37488 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37492 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37493 if(config[region]){
37494 config[region].region = region;
37495 this.addRegion(config[region]);
37501 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37503 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37505 parent : false, // this might point to a 'nest' or a ???
37508 * Creates and adds a new region if it doesn't already exist.
37509 * @param {String} target The target region key (north, south, east, west or center).
37510 * @param {Object} config The regions config object
37511 * @return {BorderLayoutRegion} The new region
37513 addRegion : function(config)
37515 if(!this.regions[config.region]){
37516 var r = this.factory(config);
37517 this.bindRegion(r);
37519 return this.regions[config.region];
37523 bindRegion : function(r){
37524 this.regions[r.config.region] = r;
37526 r.on("visibilitychange", this.layout, this);
37527 r.on("paneladded", this.layout, this);
37528 r.on("panelremoved", this.layout, this);
37529 r.on("invalidated", this.layout, this);
37530 r.on("resized", this.onRegionResized, this);
37531 r.on("collapsed", this.onRegionCollapsed, this);
37532 r.on("expanded", this.onRegionExpanded, this);
37536 * Performs a layout update.
37538 layout : function()
37540 if(this.updating) {
37544 // render all the rebions if they have not been done alreayd?
37545 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37546 if(this.regions[region] && !this.regions[region].bodyEl){
37547 this.regions[region].onRender(this.el)
37551 var size = this.getViewSize();
37552 var w = size.width;
37553 var h = size.height;
37558 //var x = 0, y = 0;
37560 var rs = this.regions;
37561 var north = rs["north"];
37562 var south = rs["south"];
37563 var west = rs["west"];
37564 var east = rs["east"];
37565 var center = rs["center"];
37566 //if(this.hideOnLayout){ // not supported anymore
37567 //c.el.setStyle("display", "none");
37569 if(north && north.isVisible()){
37570 var b = north.getBox();
37571 var m = north.getMargins();
37572 b.width = w - (m.left+m.right);
37575 centerY = b.height + b.y + m.bottom;
37576 centerH -= centerY;
37577 north.updateBox(this.safeBox(b));
37579 if(south && south.isVisible()){
37580 var b = south.getBox();
37581 var m = south.getMargins();
37582 b.width = w - (m.left+m.right);
37584 var totalHeight = (b.height + m.top + m.bottom);
37585 b.y = h - totalHeight + m.top;
37586 centerH -= totalHeight;
37587 south.updateBox(this.safeBox(b));
37589 if(west && west.isVisible()){
37590 var b = west.getBox();
37591 var m = west.getMargins();
37592 b.height = centerH - (m.top+m.bottom);
37594 b.y = centerY + m.top;
37595 var totalWidth = (b.width + m.left + m.right);
37596 centerX += totalWidth;
37597 centerW -= totalWidth;
37598 west.updateBox(this.safeBox(b));
37600 if(east && east.isVisible()){
37601 var b = east.getBox();
37602 var m = east.getMargins();
37603 b.height = centerH - (m.top+m.bottom);
37604 var totalWidth = (b.width + m.left + m.right);
37605 b.x = w - totalWidth + m.left;
37606 b.y = centerY + m.top;
37607 centerW -= totalWidth;
37608 east.updateBox(this.safeBox(b));
37611 var m = center.getMargins();
37613 x: centerX + m.left,
37614 y: centerY + m.top,
37615 width: centerW - (m.left+m.right),
37616 height: centerH - (m.top+m.bottom)
37618 //if(this.hideOnLayout){
37619 //center.el.setStyle("display", "block");
37621 center.updateBox(this.safeBox(centerBox));
37624 this.fireEvent("layout", this);
37628 safeBox : function(box){
37629 box.width = Math.max(0, box.width);
37630 box.height = Math.max(0, box.height);
37635 * Adds a ContentPanel (or subclass) to this layout.
37636 * @param {String} target The target region key (north, south, east, west or center).
37637 * @param {Roo.ContentPanel} panel The panel to add
37638 * @return {Roo.ContentPanel} The added panel
37640 add : function(target, panel){
37642 target = target.toLowerCase();
37643 return this.regions[target].add(panel);
37647 * Remove a ContentPanel (or subclass) to this layout.
37648 * @param {String} target The target region key (north, south, east, west or center).
37649 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37650 * @return {Roo.ContentPanel} The removed panel
37652 remove : function(target, panel){
37653 target = target.toLowerCase();
37654 return this.regions[target].remove(panel);
37658 * Searches all regions for a panel with the specified id
37659 * @param {String} panelId
37660 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37662 findPanel : function(panelId){
37663 var rs = this.regions;
37664 for(var target in rs){
37665 if(typeof rs[target] != "function"){
37666 var p = rs[target].getPanel(panelId);
37676 * Searches all regions for a panel with the specified id and activates (shows) it.
37677 * @param {String/ContentPanel} panelId The panels id or the panel itself
37678 * @return {Roo.ContentPanel} The shown panel or null
37680 showPanel : function(panelId) {
37681 var rs = this.regions;
37682 for(var target in rs){
37683 var r = rs[target];
37684 if(typeof r != "function"){
37685 if(r.hasPanel(panelId)){
37686 return r.showPanel(panelId);
37694 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37695 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37698 restoreState : function(provider){
37700 provider = Roo.state.Manager;
37702 var sm = new Roo.LayoutStateManager();
37703 sm.init(this, provider);
37709 * Adds a xtype elements to the layout.
37713 xtype : 'ContentPanel',
37720 xtype : 'NestedLayoutPanel',
37726 items : [ ... list of content panels or nested layout panels.. ]
37730 * @param {Object} cfg Xtype definition of item to add.
37732 addxtype : function(cfg)
37734 // basically accepts a pannel...
37735 // can accept a layout region..!?!?
37736 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37739 // theory? children can only be panels??
37741 //if (!cfg.xtype.match(/Panel$/)) {
37746 if (typeof(cfg.region) == 'undefined') {
37747 Roo.log("Failed to add Panel, region was not set");
37751 var region = cfg.region;
37757 xitems = cfg.items;
37762 if ( region == 'center') {
37763 Roo.log("Center: " + cfg.title);
37769 case 'Content': // ContentPanel (el, cfg)
37770 case 'Scroll': // ContentPanel (el, cfg)
37772 cfg.autoCreate = cfg.autoCreate || true;
37773 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37775 // var el = this.el.createChild();
37776 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37779 this.add(region, ret);
37783 case 'TreePanel': // our new panel!
37784 cfg.el = this.el.createChild();
37785 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37786 this.add(region, ret);
37791 // create a new Layout (which is a Border Layout...
37793 var clayout = cfg.layout;
37794 clayout.el = this.el.createChild();
37795 clayout.items = clayout.items || [];
37799 // replace this exitems with the clayout ones..
37800 xitems = clayout.items;
37802 // force background off if it's in center...
37803 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37804 cfg.background = false;
37806 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37809 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37810 //console.log('adding nested layout panel ' + cfg.toSource());
37811 this.add(region, ret);
37812 nb = {}; /// find first...
37817 // needs grid and region
37819 //var el = this.getRegion(region).el.createChild();
37821 *var el = this.el.createChild();
37822 // create the grid first...
37823 cfg.grid.container = el;
37824 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37827 if (region == 'center' && this.active ) {
37828 cfg.background = false;
37831 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37833 this.add(region, ret);
37835 if (cfg.background) {
37836 // render grid on panel activation (if panel background)
37837 ret.on('activate', function(gp) {
37838 if (!gp.grid.rendered) {
37839 // gp.grid.render(el);
37843 // cfg.grid.render(el);
37849 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37850 // it was the old xcomponent building that caused this before.
37851 // espeically if border is the top element in the tree.
37861 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37863 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37864 this.add(region, ret);
37868 throw "Can not add '" + cfg.xtype + "' to Border";
37874 this.beginUpdate();
37878 Roo.each(xitems, function(i) {
37879 region = nb && i.region ? i.region : false;
37881 var add = ret.addxtype(i);
37884 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37885 if (!i.background) {
37886 abn[region] = nb[region] ;
37893 // make the last non-background panel active..
37894 //if (nb) { Roo.log(abn); }
37897 for(var r in abn) {
37898 region = this.getRegion(r);
37900 // tried using nb[r], but it does not work..
37902 region.showPanel(abn[r]);
37913 factory : function(cfg)
37916 var validRegions = Roo.bootstrap.layout.Border.regions;
37918 var target = cfg.region;
37921 var r = Roo.bootstrap.layout;
37925 return new r.North(cfg);
37927 return new r.South(cfg);
37929 return new r.East(cfg);
37931 return new r.West(cfg);
37933 return new r.Center(cfg);
37935 throw 'Layout region "'+target+'" not supported.';
37942 * Ext JS Library 1.1.1
37943 * Copyright(c) 2006-2007, Ext JS, LLC.
37945 * Originally Released Under LGPL - original licence link has changed is not relivant.
37948 * <script type="text/javascript">
37952 * @class Roo.bootstrap.layout.Basic
37953 * @extends Roo.util.Observable
37954 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37955 * and does not have a titlebar, tabs or any other features. All it does is size and position
37956 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37957 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37958 * @cfg {string} region the region that it inhabits..
37959 * @cfg {bool} skipConfig skip config?
37963 Roo.bootstrap.layout.Basic = function(config){
37965 this.mgr = config.mgr;
37967 this.position = config.region;
37969 var skipConfig = config.skipConfig;
37973 * @scope Roo.BasicLayoutRegion
37977 * @event beforeremove
37978 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37979 * @param {Roo.LayoutRegion} this
37980 * @param {Roo.ContentPanel} panel The panel
37981 * @param {Object} e The cancel event object
37983 "beforeremove" : true,
37985 * @event invalidated
37986 * Fires when the layout for this region is changed.
37987 * @param {Roo.LayoutRegion} this
37989 "invalidated" : true,
37991 * @event visibilitychange
37992 * Fires when this region is shown or hidden
37993 * @param {Roo.LayoutRegion} this
37994 * @param {Boolean} visibility true or false
37996 "visibilitychange" : true,
37998 * @event paneladded
37999 * Fires when a panel is added.
38000 * @param {Roo.LayoutRegion} this
38001 * @param {Roo.ContentPanel} panel The panel
38003 "paneladded" : true,
38005 * @event panelremoved
38006 * Fires when a panel is removed.
38007 * @param {Roo.LayoutRegion} this
38008 * @param {Roo.ContentPanel} panel The panel
38010 "panelremoved" : true,
38012 * @event beforecollapse
38013 * Fires when this region before collapse.
38014 * @param {Roo.LayoutRegion} this
38016 "beforecollapse" : true,
38019 * Fires when this region is collapsed.
38020 * @param {Roo.LayoutRegion} this
38022 "collapsed" : true,
38025 * Fires when this region is expanded.
38026 * @param {Roo.LayoutRegion} this
38031 * Fires when this region is slid into view.
38032 * @param {Roo.LayoutRegion} this
38034 "slideshow" : true,
38037 * Fires when this region slides out of view.
38038 * @param {Roo.LayoutRegion} this
38040 "slidehide" : true,
38042 * @event panelactivated
38043 * Fires when a panel is activated.
38044 * @param {Roo.LayoutRegion} this
38045 * @param {Roo.ContentPanel} panel The activated panel
38047 "panelactivated" : true,
38050 * Fires when the user resizes this region.
38051 * @param {Roo.LayoutRegion} this
38052 * @param {Number} newSize The new size (width for east/west, height for north/south)
38056 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38057 this.panels = new Roo.util.MixedCollection();
38058 this.panels.getKey = this.getPanelId.createDelegate(this);
38060 this.activePanel = null;
38061 // ensure listeners are added...
38063 if (config.listeners || config.events) {
38064 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38065 listeners : config.listeners || {},
38066 events : config.events || {}
38070 if(skipConfig !== true){
38071 this.applyConfig(config);
38075 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38077 getPanelId : function(p){
38081 applyConfig : function(config){
38082 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38083 this.config = config;
38088 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38089 * the width, for horizontal (north, south) the height.
38090 * @param {Number} newSize The new width or height
38092 resizeTo : function(newSize){
38093 var el = this.el ? this.el :
38094 (this.activePanel ? this.activePanel.getEl() : null);
38096 switch(this.position){
38099 el.setWidth(newSize);
38100 this.fireEvent("resized", this, newSize);
38104 el.setHeight(newSize);
38105 this.fireEvent("resized", this, newSize);
38111 getBox : function(){
38112 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38115 getMargins : function(){
38116 return this.margins;
38119 updateBox : function(box){
38121 var el = this.activePanel.getEl();
38122 el.dom.style.left = box.x + "px";
38123 el.dom.style.top = box.y + "px";
38124 this.activePanel.setSize(box.width, box.height);
38128 * Returns the container element for this region.
38129 * @return {Roo.Element}
38131 getEl : function(){
38132 return this.activePanel;
38136 * Returns true if this region is currently visible.
38137 * @return {Boolean}
38139 isVisible : function(){
38140 return this.activePanel ? true : false;
38143 setActivePanel : function(panel){
38144 panel = this.getPanel(panel);
38145 if(this.activePanel && this.activePanel != panel){
38146 this.activePanel.setActiveState(false);
38147 this.activePanel.getEl().setLeftTop(-10000,-10000);
38149 this.activePanel = panel;
38150 panel.setActiveState(true);
38152 panel.setSize(this.box.width, this.box.height);
38154 this.fireEvent("panelactivated", this, panel);
38155 this.fireEvent("invalidated");
38159 * Show the specified panel.
38160 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38161 * @return {Roo.ContentPanel} The shown panel or null
38163 showPanel : function(panel){
38164 panel = this.getPanel(panel);
38166 this.setActivePanel(panel);
38172 * Get the active panel for this region.
38173 * @return {Roo.ContentPanel} The active panel or null
38175 getActivePanel : function(){
38176 return this.activePanel;
38180 * Add the passed ContentPanel(s)
38181 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38182 * @return {Roo.ContentPanel} The panel added (if only one was added)
38184 add : function(panel){
38185 if(arguments.length > 1){
38186 for(var i = 0, len = arguments.length; i < len; i++) {
38187 this.add(arguments[i]);
38191 if(this.hasPanel(panel)){
38192 this.showPanel(panel);
38195 var el = panel.getEl();
38196 if(el.dom.parentNode != this.mgr.el.dom){
38197 this.mgr.el.dom.appendChild(el.dom);
38199 if(panel.setRegion){
38200 panel.setRegion(this);
38202 this.panels.add(panel);
38203 el.setStyle("position", "absolute");
38204 if(!panel.background){
38205 this.setActivePanel(panel);
38206 if(this.config.initialSize && this.panels.getCount()==1){
38207 this.resizeTo(this.config.initialSize);
38210 this.fireEvent("paneladded", this, panel);
38215 * Returns true if the panel is in this region.
38216 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38217 * @return {Boolean}
38219 hasPanel : function(panel){
38220 if(typeof panel == "object"){ // must be panel obj
38221 panel = panel.getId();
38223 return this.getPanel(panel) ? true : false;
38227 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38228 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38229 * @param {Boolean} preservePanel Overrides the config preservePanel option
38230 * @return {Roo.ContentPanel} The panel that was removed
38232 remove : function(panel, preservePanel){
38233 panel = this.getPanel(panel);
38238 this.fireEvent("beforeremove", this, panel, e);
38239 if(e.cancel === true){
38242 var panelId = panel.getId();
38243 this.panels.removeKey(panelId);
38248 * Returns the panel specified or null if it's not in this region.
38249 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38250 * @return {Roo.ContentPanel}
38252 getPanel : function(id){
38253 if(typeof id == "object"){ // must be panel obj
38256 return this.panels.get(id);
38260 * Returns this regions position (north/south/east/west/center).
38263 getPosition: function(){
38264 return this.position;
38268 * Ext JS Library 1.1.1
38269 * Copyright(c) 2006-2007, Ext JS, LLC.
38271 * Originally Released Under LGPL - original licence link has changed is not relivant.
38274 * <script type="text/javascript">
38278 * @class Roo.bootstrap.layout.Region
38279 * @extends Roo.bootstrap.layout.Basic
38280 * This class represents a region in a layout manager.
38282 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38283 * @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})
38284 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38285 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38286 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38287 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38288 * @cfg {String} title The title for the region (overrides panel titles)
38289 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38290 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38291 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38292 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38293 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38294 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38295 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38296 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38297 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38298 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38300 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38301 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38302 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38303 * @cfg {Number} width For East/West panels
38304 * @cfg {Number} height For North/South panels
38305 * @cfg {Boolean} split To show the splitter
38306 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38308 * @cfg {string} cls Extra CSS classes to add to region
38310 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38311 * @cfg {string} region the region that it inhabits..
38314 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38315 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38317 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38318 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38319 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38321 Roo.bootstrap.layout.Region = function(config)
38323 this.applyConfig(config);
38325 var mgr = config.mgr;
38326 var pos = config.region;
38327 config.skipConfig = true;
38328 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38331 this.onRender(mgr.el);
38334 this.visible = true;
38335 this.collapsed = false;
38336 this.unrendered_panels = [];
38339 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38341 position: '', // set by wrapper (eg. north/south etc..)
38342 unrendered_panels : null, // unrendered panels.
38344 tabPosition : false,
38346 mgr: false, // points to 'Border'
38349 createBody : function(){
38350 /** This region's body element
38351 * @type Roo.Element */
38352 this.bodyEl = this.el.createChild({
38354 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38358 onRender: function(ctr, pos)
38360 var dh = Roo.DomHelper;
38361 /** This region's container element
38362 * @type Roo.Element */
38363 this.el = dh.append(ctr.dom, {
38365 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38367 /** This region's title element
38368 * @type Roo.Element */
38370 this.titleEl = dh.append(this.el.dom, {
38372 unselectable: "on",
38373 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38375 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38376 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38380 this.titleEl.enableDisplayMode();
38381 /** This region's title text element
38382 * @type HTMLElement */
38383 this.titleTextEl = this.titleEl.dom.firstChild;
38384 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38386 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38387 this.closeBtn.enableDisplayMode();
38388 this.closeBtn.on("click", this.closeClicked, this);
38389 this.closeBtn.hide();
38391 this.createBody(this.config);
38392 if(this.config.hideWhenEmpty){
38394 this.on("paneladded", this.validateVisibility, this);
38395 this.on("panelremoved", this.validateVisibility, this);
38397 if(this.autoScroll){
38398 this.bodyEl.setStyle("overflow", "auto");
38400 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38402 //if(c.titlebar !== false){
38403 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38404 this.titleEl.hide();
38406 this.titleEl.show();
38407 if(this.config.title){
38408 this.titleTextEl.innerHTML = this.config.title;
38412 if(this.config.collapsed){
38413 this.collapse(true);
38415 if(this.config.hidden){
38419 if (this.unrendered_panels && this.unrendered_panels.length) {
38420 for (var i =0;i< this.unrendered_panels.length; i++) {
38421 this.add(this.unrendered_panels[i]);
38423 this.unrendered_panels = null;
38429 applyConfig : function(c)
38432 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38433 var dh = Roo.DomHelper;
38434 if(c.titlebar !== false){
38435 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38436 this.collapseBtn.on("click", this.collapse, this);
38437 this.collapseBtn.enableDisplayMode();
38439 if(c.showPin === true || this.showPin){
38440 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38441 this.stickBtn.enableDisplayMode();
38442 this.stickBtn.on("click", this.expand, this);
38443 this.stickBtn.hide();
38448 /** This region's collapsed element
38449 * @type Roo.Element */
38452 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38453 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38456 if(c.floatable !== false){
38457 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38458 this.collapsedEl.on("click", this.collapseClick, this);
38461 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38462 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38463 id: "message", unselectable: "on", style:{"float":"left"}});
38464 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38466 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38467 this.expandBtn.on("click", this.expand, this);
38471 if(this.collapseBtn){
38472 this.collapseBtn.setVisible(c.collapsible == true);
38475 this.cmargins = c.cmargins || this.cmargins ||
38476 (this.position == "west" || this.position == "east" ?
38477 {top: 0, left: 2, right:2, bottom: 0} :
38478 {top: 2, left: 0, right:0, bottom: 2});
38480 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38483 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38485 this.autoScroll = c.autoScroll || false;
38490 this.duration = c.duration || .30;
38491 this.slideDuration = c.slideDuration || .45;
38496 * Returns true if this region is currently visible.
38497 * @return {Boolean}
38499 isVisible : function(){
38500 return this.visible;
38504 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38505 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38507 //setCollapsedTitle : function(title){
38508 // title = title || " ";
38509 // if(this.collapsedTitleTextEl){
38510 // this.collapsedTitleTextEl.innerHTML = title;
38514 getBox : function(){
38516 // if(!this.collapsed){
38517 b = this.el.getBox(false, true);
38519 // b = this.collapsedEl.getBox(false, true);
38524 getMargins : function(){
38525 return this.margins;
38526 //return this.collapsed ? this.cmargins : this.margins;
38529 highlight : function(){
38530 this.el.addClass("x-layout-panel-dragover");
38533 unhighlight : function(){
38534 this.el.removeClass("x-layout-panel-dragover");
38537 updateBox : function(box)
38539 if (!this.bodyEl) {
38540 return; // not rendered yet..
38544 if(!this.collapsed){
38545 this.el.dom.style.left = box.x + "px";
38546 this.el.dom.style.top = box.y + "px";
38547 this.updateBody(box.width, box.height);
38549 this.collapsedEl.dom.style.left = box.x + "px";
38550 this.collapsedEl.dom.style.top = box.y + "px";
38551 this.collapsedEl.setSize(box.width, box.height);
38554 this.tabs.autoSizeTabs();
38558 updateBody : function(w, h)
38561 this.el.setWidth(w);
38562 w -= this.el.getBorderWidth("rl");
38563 if(this.config.adjustments){
38564 w += this.config.adjustments[0];
38567 if(h !== null && h > 0){
38568 this.el.setHeight(h);
38569 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38570 h -= this.el.getBorderWidth("tb");
38571 if(this.config.adjustments){
38572 h += this.config.adjustments[1];
38574 this.bodyEl.setHeight(h);
38576 h = this.tabs.syncHeight(h);
38579 if(this.panelSize){
38580 w = w !== null ? w : this.panelSize.width;
38581 h = h !== null ? h : this.panelSize.height;
38583 if(this.activePanel){
38584 var el = this.activePanel.getEl();
38585 w = w !== null ? w : el.getWidth();
38586 h = h !== null ? h : el.getHeight();
38587 this.panelSize = {width: w, height: h};
38588 this.activePanel.setSize(w, h);
38590 if(Roo.isIE && this.tabs){
38591 this.tabs.el.repaint();
38596 * Returns the container element for this region.
38597 * @return {Roo.Element}
38599 getEl : function(){
38604 * Hides this region.
38607 //if(!this.collapsed){
38608 this.el.dom.style.left = "-2000px";
38611 // this.collapsedEl.dom.style.left = "-2000px";
38612 // this.collapsedEl.hide();
38614 this.visible = false;
38615 this.fireEvent("visibilitychange", this, false);
38619 * Shows this region if it was previously hidden.
38622 //if(!this.collapsed){
38625 // this.collapsedEl.show();
38627 this.visible = true;
38628 this.fireEvent("visibilitychange", this, true);
38631 closeClicked : function(){
38632 if(this.activePanel){
38633 this.remove(this.activePanel);
38637 collapseClick : function(e){
38639 e.stopPropagation();
38642 e.stopPropagation();
38648 * Collapses this region.
38649 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38652 collapse : function(skipAnim, skipCheck = false){
38653 if(this.collapsed) {
38657 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38659 this.collapsed = true;
38661 this.split.el.hide();
38663 if(this.config.animate && skipAnim !== true){
38664 this.fireEvent("invalidated", this);
38665 this.animateCollapse();
38667 this.el.setLocation(-20000,-20000);
38669 this.collapsedEl.show();
38670 this.fireEvent("collapsed", this);
38671 this.fireEvent("invalidated", this);
38677 animateCollapse : function(){
38682 * Expands this region if it was previously collapsed.
38683 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38684 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38687 expand : function(e, skipAnim){
38689 e.stopPropagation();
38691 if(!this.collapsed || this.el.hasActiveFx()) {
38695 this.afterSlideIn();
38698 this.collapsed = false;
38699 if(this.config.animate && skipAnim !== true){
38700 this.animateExpand();
38704 this.split.el.show();
38706 this.collapsedEl.setLocation(-2000,-2000);
38707 this.collapsedEl.hide();
38708 this.fireEvent("invalidated", this);
38709 this.fireEvent("expanded", this);
38713 animateExpand : function(){
38717 initTabs : function()
38719 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38721 var ts = new Roo.bootstrap.panel.Tabs({
38722 el: this.bodyEl.dom,
38724 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38725 disableTooltips: this.config.disableTabTips,
38726 toolbar : this.config.toolbar
38729 if(this.config.hideTabs){
38730 ts.stripWrap.setDisplayed(false);
38733 ts.resizeTabs = this.config.resizeTabs === true;
38734 ts.minTabWidth = this.config.minTabWidth || 40;
38735 ts.maxTabWidth = this.config.maxTabWidth || 250;
38736 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38737 ts.monitorResize = false;
38738 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38739 ts.bodyEl.addClass('roo-layout-tabs-body');
38740 this.panels.each(this.initPanelAsTab, this);
38743 initPanelAsTab : function(panel){
38744 var ti = this.tabs.addTab(
38748 this.config.closeOnTab && panel.isClosable(),
38751 if(panel.tabTip !== undefined){
38752 ti.setTooltip(panel.tabTip);
38754 ti.on("activate", function(){
38755 this.setActivePanel(panel);
38758 if(this.config.closeOnTab){
38759 ti.on("beforeclose", function(t, e){
38761 this.remove(panel);
38765 panel.tabItem = ti;
38770 updatePanelTitle : function(panel, title)
38772 if(this.activePanel == panel){
38773 this.updateTitle(title);
38776 var ti = this.tabs.getTab(panel.getEl().id);
38778 if(panel.tabTip !== undefined){
38779 ti.setTooltip(panel.tabTip);
38784 updateTitle : function(title){
38785 if(this.titleTextEl && !this.config.title){
38786 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38790 setActivePanel : function(panel)
38792 panel = this.getPanel(panel);
38793 if(this.activePanel && this.activePanel != panel){
38794 if(this.activePanel.setActiveState(false) === false){
38798 this.activePanel = panel;
38799 panel.setActiveState(true);
38800 if(this.panelSize){
38801 panel.setSize(this.panelSize.width, this.panelSize.height);
38804 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38806 this.updateTitle(panel.getTitle());
38808 this.fireEvent("invalidated", this);
38810 this.fireEvent("panelactivated", this, panel);
38814 * Shows the specified panel.
38815 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38816 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38818 showPanel : function(panel)
38820 panel = this.getPanel(panel);
38823 var tab = this.tabs.getTab(panel.getEl().id);
38824 if(tab.isHidden()){
38825 this.tabs.unhideTab(tab.id);
38829 this.setActivePanel(panel);
38836 * Get the active panel for this region.
38837 * @return {Roo.ContentPanel} The active panel or null
38839 getActivePanel : function(){
38840 return this.activePanel;
38843 validateVisibility : function(){
38844 if(this.panels.getCount() < 1){
38845 this.updateTitle(" ");
38846 this.closeBtn.hide();
38849 if(!this.isVisible()){
38856 * Adds the passed ContentPanel(s) to this region.
38857 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38858 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38860 add : function(panel)
38862 if(arguments.length > 1){
38863 for(var i = 0, len = arguments.length; i < len; i++) {
38864 this.add(arguments[i]);
38869 // if we have not been rendered yet, then we can not really do much of this..
38870 if (!this.bodyEl) {
38871 this.unrendered_panels.push(panel);
38878 if(this.hasPanel(panel)){
38879 this.showPanel(panel);
38882 panel.setRegion(this);
38883 this.panels.add(panel);
38884 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38885 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38886 // and hide them... ???
38887 this.bodyEl.dom.appendChild(panel.getEl().dom);
38888 if(panel.background !== true){
38889 this.setActivePanel(panel);
38891 this.fireEvent("paneladded", this, panel);
38898 this.initPanelAsTab(panel);
38902 if(panel.background !== true){
38903 this.tabs.activate(panel.getEl().id);
38905 this.fireEvent("paneladded", this, panel);
38910 * Hides the tab for the specified panel.
38911 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38913 hidePanel : function(panel){
38914 if(this.tabs && (panel = this.getPanel(panel))){
38915 this.tabs.hideTab(panel.getEl().id);
38920 * Unhides the tab for a previously hidden panel.
38921 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38923 unhidePanel : function(panel){
38924 if(this.tabs && (panel = this.getPanel(panel))){
38925 this.tabs.unhideTab(panel.getEl().id);
38929 clearPanels : function(){
38930 while(this.panels.getCount() > 0){
38931 this.remove(this.panels.first());
38936 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38937 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38938 * @param {Boolean} preservePanel Overrides the config preservePanel option
38939 * @return {Roo.ContentPanel} The panel that was removed
38941 remove : function(panel, preservePanel)
38943 panel = this.getPanel(panel);
38948 this.fireEvent("beforeremove", this, panel, e);
38949 if(e.cancel === true){
38952 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38953 var panelId = panel.getId();
38954 this.panels.removeKey(panelId);
38956 document.body.appendChild(panel.getEl().dom);
38959 this.tabs.removeTab(panel.getEl().id);
38960 }else if (!preservePanel){
38961 this.bodyEl.dom.removeChild(panel.getEl().dom);
38963 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38964 var p = this.panels.first();
38965 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38966 tempEl.appendChild(p.getEl().dom);
38967 this.bodyEl.update("");
38968 this.bodyEl.dom.appendChild(p.getEl().dom);
38970 this.updateTitle(p.getTitle());
38972 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38973 this.setActivePanel(p);
38975 panel.setRegion(null);
38976 if(this.activePanel == panel){
38977 this.activePanel = null;
38979 if(this.config.autoDestroy !== false && preservePanel !== true){
38980 try{panel.destroy();}catch(e){}
38982 this.fireEvent("panelremoved", this, panel);
38987 * Returns the TabPanel component used by this region
38988 * @return {Roo.TabPanel}
38990 getTabs : function(){
38994 createTool : function(parentEl, className){
38995 var btn = Roo.DomHelper.append(parentEl, {
38997 cls: "x-layout-tools-button",
39000 cls: "roo-layout-tools-button-inner " + className,
39004 btn.addClassOnOver("roo-layout-tools-button-over");
39009 * Ext JS Library 1.1.1
39010 * Copyright(c) 2006-2007, Ext JS, LLC.
39012 * Originally Released Under LGPL - original licence link has changed is not relivant.
39015 * <script type="text/javascript">
39021 * @class Roo.SplitLayoutRegion
39022 * @extends Roo.LayoutRegion
39023 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39025 Roo.bootstrap.layout.Split = function(config){
39026 this.cursor = config.cursor;
39027 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39030 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39032 splitTip : "Drag to resize.",
39033 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39034 useSplitTips : false,
39036 applyConfig : function(config){
39037 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39040 onRender : function(ctr,pos) {
39042 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39043 if(!this.config.split){
39048 var splitEl = Roo.DomHelper.append(ctr.dom, {
39050 id: this.el.id + "-split",
39051 cls: "roo-layout-split roo-layout-split-"+this.position,
39054 /** The SplitBar for this region
39055 * @type Roo.SplitBar */
39056 // does not exist yet...
39057 Roo.log([this.position, this.orientation]);
39059 this.split = new Roo.bootstrap.SplitBar({
39060 dragElement : splitEl,
39061 resizingElement: this.el,
39062 orientation : this.orientation
39065 this.split.on("moved", this.onSplitMove, this);
39066 this.split.useShim = this.config.useShim === true;
39067 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39068 if(this.useSplitTips){
39069 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39071 //if(config.collapsible){
39072 // this.split.el.on("dblclick", this.collapse, this);
39075 if(typeof this.config.minSize != "undefined"){
39076 this.split.minSize = this.config.minSize;
39078 if(typeof this.config.maxSize != "undefined"){
39079 this.split.maxSize = this.config.maxSize;
39081 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39082 this.hideSplitter();
39087 getHMaxSize : function(){
39088 var cmax = this.config.maxSize || 10000;
39089 var center = this.mgr.getRegion("center");
39090 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39093 getVMaxSize : function(){
39094 var cmax = this.config.maxSize || 10000;
39095 var center = this.mgr.getRegion("center");
39096 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39099 onSplitMove : function(split, newSize){
39100 this.fireEvent("resized", this, newSize);
39104 * Returns the {@link Roo.SplitBar} for this region.
39105 * @return {Roo.SplitBar}
39107 getSplitBar : function(){
39112 this.hideSplitter();
39113 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39116 hideSplitter : function(){
39118 this.split.el.setLocation(-2000,-2000);
39119 this.split.el.hide();
39125 this.split.el.show();
39127 Roo.bootstrap.layout.Split.superclass.show.call(this);
39130 beforeSlide: function(){
39131 if(Roo.isGecko){// firefox overflow auto bug workaround
39132 this.bodyEl.clip();
39134 this.tabs.bodyEl.clip();
39136 if(this.activePanel){
39137 this.activePanel.getEl().clip();
39139 if(this.activePanel.beforeSlide){
39140 this.activePanel.beforeSlide();
39146 afterSlide : function(){
39147 if(Roo.isGecko){// firefox overflow auto bug workaround
39148 this.bodyEl.unclip();
39150 this.tabs.bodyEl.unclip();
39152 if(this.activePanel){
39153 this.activePanel.getEl().unclip();
39154 if(this.activePanel.afterSlide){
39155 this.activePanel.afterSlide();
39161 initAutoHide : function(){
39162 if(this.autoHide !== false){
39163 if(!this.autoHideHd){
39164 var st = new Roo.util.DelayedTask(this.slideIn, this);
39165 this.autoHideHd = {
39166 "mouseout": function(e){
39167 if(!e.within(this.el, true)){
39171 "mouseover" : function(e){
39177 this.el.on(this.autoHideHd);
39181 clearAutoHide : function(){
39182 if(this.autoHide !== false){
39183 this.el.un("mouseout", this.autoHideHd.mouseout);
39184 this.el.un("mouseover", this.autoHideHd.mouseover);
39188 clearMonitor : function(){
39189 Roo.get(document).un("click", this.slideInIf, this);
39192 // these names are backwards but not changed for compat
39193 slideOut : function(){
39194 if(this.isSlid || this.el.hasActiveFx()){
39197 this.isSlid = true;
39198 if(this.collapseBtn){
39199 this.collapseBtn.hide();
39201 this.closeBtnState = this.closeBtn.getStyle('display');
39202 this.closeBtn.hide();
39204 this.stickBtn.show();
39207 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39208 this.beforeSlide();
39209 this.el.setStyle("z-index", 10001);
39210 this.el.slideIn(this.getSlideAnchor(), {
39211 callback: function(){
39213 this.initAutoHide();
39214 Roo.get(document).on("click", this.slideInIf, this);
39215 this.fireEvent("slideshow", this);
39222 afterSlideIn : function(){
39223 this.clearAutoHide();
39224 this.isSlid = false;
39225 this.clearMonitor();
39226 this.el.setStyle("z-index", "");
39227 if(this.collapseBtn){
39228 this.collapseBtn.show();
39230 this.closeBtn.setStyle('display', this.closeBtnState);
39232 this.stickBtn.hide();
39234 this.fireEvent("slidehide", this);
39237 slideIn : function(cb){
39238 if(!this.isSlid || this.el.hasActiveFx()){
39242 this.isSlid = false;
39243 this.beforeSlide();
39244 this.el.slideOut(this.getSlideAnchor(), {
39245 callback: function(){
39246 this.el.setLeftTop(-10000, -10000);
39248 this.afterSlideIn();
39256 slideInIf : function(e){
39257 if(!e.within(this.el)){
39262 animateCollapse : function(){
39263 this.beforeSlide();
39264 this.el.setStyle("z-index", 20000);
39265 var anchor = this.getSlideAnchor();
39266 this.el.slideOut(anchor, {
39267 callback : function(){
39268 this.el.setStyle("z-index", "");
39269 this.collapsedEl.slideIn(anchor, {duration:.3});
39271 this.el.setLocation(-10000,-10000);
39273 this.fireEvent("collapsed", this);
39280 animateExpand : function(){
39281 this.beforeSlide();
39282 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39283 this.el.setStyle("z-index", 20000);
39284 this.collapsedEl.hide({
39287 this.el.slideIn(this.getSlideAnchor(), {
39288 callback : function(){
39289 this.el.setStyle("z-index", "");
39292 this.split.el.show();
39294 this.fireEvent("invalidated", this);
39295 this.fireEvent("expanded", this);
39323 getAnchor : function(){
39324 return this.anchors[this.position];
39327 getCollapseAnchor : function(){
39328 return this.canchors[this.position];
39331 getSlideAnchor : function(){
39332 return this.sanchors[this.position];
39335 getAlignAdj : function(){
39336 var cm = this.cmargins;
39337 switch(this.position){
39353 getExpandAdj : function(){
39354 var c = this.collapsedEl, cm = this.cmargins;
39355 switch(this.position){
39357 return [-(cm.right+c.getWidth()+cm.left), 0];
39360 return [cm.right+c.getWidth()+cm.left, 0];
39363 return [0, -(cm.top+cm.bottom+c.getHeight())];
39366 return [0, cm.top+cm.bottom+c.getHeight()];
39372 * Ext JS Library 1.1.1
39373 * Copyright(c) 2006-2007, Ext JS, LLC.
39375 * Originally Released Under LGPL - original licence link has changed is not relivant.
39378 * <script type="text/javascript">
39381 * These classes are private internal classes
39383 Roo.bootstrap.layout.Center = function(config){
39384 config.region = "center";
39385 Roo.bootstrap.layout.Region.call(this, config);
39386 this.visible = true;
39387 this.minWidth = config.minWidth || 20;
39388 this.minHeight = config.minHeight || 20;
39391 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39393 // center panel can't be hidden
39397 // center panel can't be hidden
39400 getMinWidth: function(){
39401 return this.minWidth;
39404 getMinHeight: function(){
39405 return this.minHeight;
39419 Roo.bootstrap.layout.North = function(config)
39421 config.region = 'north';
39422 config.cursor = 'n-resize';
39424 Roo.bootstrap.layout.Split.call(this, config);
39428 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39429 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39430 this.split.el.addClass("roo-layout-split-v");
39432 //var size = config.initialSize || config.height;
39433 //if(this.el && typeof size != "undefined"){
39434 // this.el.setHeight(size);
39437 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39439 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39442 onRender : function(ctr, pos)
39444 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39445 var size = this.config.initialSize || this.config.height;
39446 if(this.el && typeof size != "undefined"){
39447 this.el.setHeight(size);
39452 getBox : function(){
39453 if(this.collapsed){
39454 return this.collapsedEl.getBox();
39456 var box = this.el.getBox();
39458 box.height += this.split.el.getHeight();
39463 updateBox : function(box){
39464 if(this.split && !this.collapsed){
39465 box.height -= this.split.el.getHeight();
39466 this.split.el.setLeft(box.x);
39467 this.split.el.setTop(box.y+box.height);
39468 this.split.el.setWidth(box.width);
39470 if(this.collapsed){
39471 this.updateBody(box.width, null);
39473 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39481 Roo.bootstrap.layout.South = function(config){
39482 config.region = 'south';
39483 config.cursor = 's-resize';
39484 Roo.bootstrap.layout.Split.call(this, config);
39486 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39487 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39488 this.split.el.addClass("roo-layout-split-v");
39493 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39494 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39496 onRender : function(ctr, pos)
39498 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39499 var size = this.config.initialSize || this.config.height;
39500 if(this.el && typeof size != "undefined"){
39501 this.el.setHeight(size);
39506 getBox : function(){
39507 if(this.collapsed){
39508 return this.collapsedEl.getBox();
39510 var box = this.el.getBox();
39512 var sh = this.split.el.getHeight();
39519 updateBox : function(box){
39520 if(this.split && !this.collapsed){
39521 var sh = this.split.el.getHeight();
39524 this.split.el.setLeft(box.x);
39525 this.split.el.setTop(box.y-sh);
39526 this.split.el.setWidth(box.width);
39528 if(this.collapsed){
39529 this.updateBody(box.width, null);
39531 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39535 Roo.bootstrap.layout.East = function(config){
39536 config.region = "east";
39537 config.cursor = "e-resize";
39538 Roo.bootstrap.layout.Split.call(this, config);
39540 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39541 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39542 this.split.el.addClass("roo-layout-split-h");
39546 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39547 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39549 onRender : function(ctr, pos)
39551 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39552 var size = this.config.initialSize || this.config.width;
39553 if(this.el && typeof size != "undefined"){
39554 this.el.setWidth(size);
39559 getBox : function(){
39560 if(this.collapsed){
39561 return this.collapsedEl.getBox();
39563 var box = this.el.getBox();
39565 var sw = this.split.el.getWidth();
39572 updateBox : function(box){
39573 if(this.split && !this.collapsed){
39574 var sw = this.split.el.getWidth();
39576 this.split.el.setLeft(box.x);
39577 this.split.el.setTop(box.y);
39578 this.split.el.setHeight(box.height);
39581 if(this.collapsed){
39582 this.updateBody(null, box.height);
39584 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39588 Roo.bootstrap.layout.West = function(config){
39589 config.region = "west";
39590 config.cursor = "w-resize";
39592 Roo.bootstrap.layout.Split.call(this, config);
39594 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39595 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39596 this.split.el.addClass("roo-layout-split-h");
39600 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39601 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39603 onRender: function(ctr, pos)
39605 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39606 var size = this.config.initialSize || this.config.width;
39607 if(typeof size != "undefined"){
39608 this.el.setWidth(size);
39612 getBox : function(){
39613 if(this.collapsed){
39614 return this.collapsedEl.getBox();
39616 var box = this.el.getBox();
39617 if (box.width == 0) {
39618 box.width = this.config.width; // kludge?
39621 box.width += this.split.el.getWidth();
39626 updateBox : function(box){
39627 if(this.split && !this.collapsed){
39628 var sw = this.split.el.getWidth();
39630 this.split.el.setLeft(box.x+box.width);
39631 this.split.el.setTop(box.y);
39632 this.split.el.setHeight(box.height);
39634 if(this.collapsed){
39635 this.updateBody(null, box.height);
39637 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39639 });Roo.namespace("Roo.bootstrap.panel");/*
39641 * Ext JS Library 1.1.1
39642 * Copyright(c) 2006-2007, Ext JS, LLC.
39644 * Originally Released Under LGPL - original licence link has changed is not relivant.
39647 * <script type="text/javascript">
39650 * @class Roo.ContentPanel
39651 * @extends Roo.util.Observable
39652 * A basic ContentPanel element.
39653 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39654 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39655 * @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
39656 * @cfg {Boolean} closable True if the panel can be closed/removed
39657 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39658 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39659 * @cfg {Toolbar} toolbar A toolbar for this panel
39660 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39661 * @cfg {String} title The title for this panel
39662 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39663 * @cfg {String} url Calls {@link #setUrl} with this value
39664 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39665 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39666 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39667 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39668 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39669 * @cfg {Boolean} badges render the badges
39670 * @cfg {String} cls extra classes to use
39671 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39674 * Create a new ContentPanel.
39675 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39676 * @param {String/Object} config A string to set only the title or a config object
39677 * @param {String} content (optional) Set the HTML content for this panel
39678 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39680 Roo.bootstrap.panel.Content = function( config){
39682 this.tpl = config.tpl || false;
39684 var el = config.el;
39685 var content = config.content;
39687 if(config.autoCreate){ // xtype is available if this is called from factory
39690 this.el = Roo.get(el);
39691 if(!this.el && config && config.autoCreate){
39692 if(typeof config.autoCreate == "object"){
39693 if(!config.autoCreate.id){
39694 config.autoCreate.id = config.id||el;
39696 this.el = Roo.DomHelper.append(document.body,
39697 config.autoCreate, true);
39701 cls: (config.cls || '') +
39702 (config.background ? ' bg-' + config.background : '') +
39703 " roo-layout-inactive-content",
39706 if (config.iframe) {
39710 style : 'border: 0px',
39711 src : 'about:blank'
39717 elcfg.html = config.html;
39721 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39722 if (config.iframe) {
39723 this.iframeEl = this.el.select('iframe',true).first();
39728 this.closable = false;
39729 this.loaded = false;
39730 this.active = false;
39733 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39735 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39737 this.wrapEl = this.el; //this.el.wrap();
39739 if (config.toolbar.items) {
39740 ti = config.toolbar.items ;
39741 delete config.toolbar.items ;
39745 this.toolbar.render(this.wrapEl, 'before');
39746 for(var i =0;i < ti.length;i++) {
39747 // Roo.log(['add child', items[i]]);
39748 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39750 this.toolbar.items = nitems;
39751 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39752 delete config.toolbar;
39756 // xtype created footer. - not sure if will work as we normally have to render first..
39757 if (this.footer && !this.footer.el && this.footer.xtype) {
39758 if (!this.wrapEl) {
39759 this.wrapEl = this.el.wrap();
39762 this.footer.container = this.wrapEl.createChild();
39764 this.footer = Roo.factory(this.footer, Roo);
39769 if(typeof config == "string"){
39770 this.title = config;
39772 Roo.apply(this, config);
39776 this.resizeEl = Roo.get(this.resizeEl, true);
39778 this.resizeEl = this.el;
39780 // handle view.xtype
39788 * Fires when this panel is activated.
39789 * @param {Roo.ContentPanel} this
39793 * @event deactivate
39794 * Fires when this panel is activated.
39795 * @param {Roo.ContentPanel} this
39797 "deactivate" : true,
39801 * Fires when this panel is resized if fitToFrame is true.
39802 * @param {Roo.ContentPanel} this
39803 * @param {Number} width The width after any component adjustments
39804 * @param {Number} height The height after any component adjustments
39810 * Fires when this tab is created
39811 * @param {Roo.ContentPanel} this
39822 if(this.autoScroll && !this.iframe){
39823 this.resizeEl.setStyle("overflow", "auto");
39825 // fix randome scrolling
39826 //this.el.on('scroll', function() {
39827 // Roo.log('fix random scolling');
39828 // this.scrollTo('top',0);
39831 content = content || this.content;
39833 this.setContent(content);
39835 if(config && config.url){
39836 this.setUrl(this.url, this.params, this.loadOnce);
39841 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39843 if (this.view && typeof(this.view.xtype) != 'undefined') {
39844 this.view.el = this.el.appendChild(document.createElement("div"));
39845 this.view = Roo.factory(this.view);
39846 this.view.render && this.view.render(false, '');
39850 this.fireEvent('render', this);
39853 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39863 setRegion : function(region){
39864 this.region = region;
39865 this.setActiveClass(region && !this.background);
39869 setActiveClass: function(state)
39872 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39873 this.el.setStyle('position','relative');
39875 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39876 this.el.setStyle('position', 'absolute');
39881 * Returns the toolbar for this Panel if one was configured.
39882 * @return {Roo.Toolbar}
39884 getToolbar : function(){
39885 return this.toolbar;
39888 setActiveState : function(active)
39890 this.active = active;
39891 this.setActiveClass(active);
39893 if(this.fireEvent("deactivate", this) === false){
39898 this.fireEvent("activate", this);
39902 * Updates this panel's element (not for iframe)
39903 * @param {String} content The new content
39904 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39906 setContent : function(content, loadScripts){
39911 this.el.update(content, loadScripts);
39914 ignoreResize : function(w, h){
39915 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39918 this.lastSize = {width: w, height: h};
39923 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39924 * @return {Roo.UpdateManager} The UpdateManager
39926 getUpdateManager : function(){
39930 return this.el.getUpdateManager();
39933 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39934 * Does not work with IFRAME contents
39935 * @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:
39938 url: "your-url.php",
39939 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39940 callback: yourFunction,
39941 scope: yourObject, //(optional scope)
39944 text: "Loading...",
39950 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39951 * 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.
39952 * @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}
39953 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39954 * @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.
39955 * @return {Roo.ContentPanel} this
39963 var um = this.el.getUpdateManager();
39964 um.update.apply(um, arguments);
39970 * 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.
39971 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39972 * @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)
39973 * @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)
39974 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39976 setUrl : function(url, params, loadOnce){
39978 this.iframeEl.dom.src = url;
39982 if(this.refreshDelegate){
39983 this.removeListener("activate", this.refreshDelegate);
39985 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39986 this.on("activate", this.refreshDelegate);
39987 return this.el.getUpdateManager();
39990 _handleRefresh : function(url, params, loadOnce){
39991 if(!loadOnce || !this.loaded){
39992 var updater = this.el.getUpdateManager();
39993 updater.update(url, params, this._setLoaded.createDelegate(this));
39997 _setLoaded : function(){
39998 this.loaded = true;
40002 * Returns this panel's id
40005 getId : function(){
40010 * Returns this panel's element - used by regiosn to add.
40011 * @return {Roo.Element}
40013 getEl : function(){
40014 return this.wrapEl || this.el;
40019 adjustForComponents : function(width, height)
40021 //Roo.log('adjustForComponents ');
40022 if(this.resizeEl != this.el){
40023 width -= this.el.getFrameWidth('lr');
40024 height -= this.el.getFrameWidth('tb');
40027 var te = this.toolbar.getEl();
40028 te.setWidth(width);
40029 height -= te.getHeight();
40032 var te = this.footer.getEl();
40033 te.setWidth(width);
40034 height -= te.getHeight();
40038 if(this.adjustments){
40039 width += this.adjustments[0];
40040 height += this.adjustments[1];
40042 return {"width": width, "height": height};
40045 setSize : function(width, height){
40046 if(this.fitToFrame && !this.ignoreResize(width, height)){
40047 if(this.fitContainer && this.resizeEl != this.el){
40048 this.el.setSize(width, height);
40050 var size = this.adjustForComponents(width, height);
40052 this.iframeEl.setSize(width,height);
40055 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40056 this.fireEvent('resize', this, size.width, size.height);
40063 * Returns this panel's title
40066 getTitle : function(){
40068 if (typeof(this.title) != 'object') {
40073 for (var k in this.title) {
40074 if (!this.title.hasOwnProperty(k)) {
40078 if (k.indexOf('-') >= 0) {
40079 var s = k.split('-');
40080 for (var i = 0; i<s.length; i++) {
40081 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40084 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40091 * Set this panel's title
40092 * @param {String} title
40094 setTitle : function(title){
40095 this.title = title;
40097 this.region.updatePanelTitle(this, title);
40102 * Returns true is this panel was configured to be closable
40103 * @return {Boolean}
40105 isClosable : function(){
40106 return this.closable;
40109 beforeSlide : function(){
40111 this.resizeEl.clip();
40114 afterSlide : function(){
40116 this.resizeEl.unclip();
40120 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40121 * Will fail silently if the {@link #setUrl} method has not been called.
40122 * This does not activate the panel, just updates its content.
40124 refresh : function(){
40125 if(this.refreshDelegate){
40126 this.loaded = false;
40127 this.refreshDelegate();
40132 * Destroys this panel
40134 destroy : function(){
40135 this.el.removeAllListeners();
40136 var tempEl = document.createElement("span");
40137 tempEl.appendChild(this.el.dom);
40138 tempEl.innerHTML = "";
40144 * form - if the content panel contains a form - this is a reference to it.
40145 * @type {Roo.form.Form}
40149 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40150 * This contains a reference to it.
40156 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40166 * @param {Object} cfg Xtype definition of item to add.
40170 getChildContainer: function () {
40171 return this.getEl();
40176 var ret = new Roo.factory(cfg);
40181 if (cfg.xtype.match(/^Form$/)) {
40184 //if (this.footer) {
40185 // el = this.footer.container.insertSibling(false, 'before');
40187 el = this.el.createChild();
40190 this.form = new Roo.form.Form(cfg);
40193 if ( this.form.allItems.length) {
40194 this.form.render(el.dom);
40198 // should only have one of theses..
40199 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40200 // views.. should not be just added - used named prop 'view''
40202 cfg.el = this.el.appendChild(document.createElement("div"));
40205 var ret = new Roo.factory(cfg);
40207 ret.render && ret.render(false, ''); // render blank..
40217 * @class Roo.bootstrap.panel.Grid
40218 * @extends Roo.bootstrap.panel.Content
40220 * Create a new GridPanel.
40221 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40222 * @param {Object} config A the config object
40228 Roo.bootstrap.panel.Grid = function(config)
40232 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40233 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40235 config.el = this.wrapper;
40236 //this.el = this.wrapper;
40238 if (config.container) {
40239 // ctor'ed from a Border/panel.grid
40242 this.wrapper.setStyle("overflow", "hidden");
40243 this.wrapper.addClass('roo-grid-container');
40248 if(config.toolbar){
40249 var tool_el = this.wrapper.createChild();
40250 this.toolbar = Roo.factory(config.toolbar);
40252 if (config.toolbar.items) {
40253 ti = config.toolbar.items ;
40254 delete config.toolbar.items ;
40258 this.toolbar.render(tool_el);
40259 for(var i =0;i < ti.length;i++) {
40260 // Roo.log(['add child', items[i]]);
40261 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40263 this.toolbar.items = nitems;
40265 delete config.toolbar;
40268 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40269 config.grid.scrollBody = true;;
40270 config.grid.monitorWindowResize = false; // turn off autosizing
40271 config.grid.autoHeight = false;
40272 config.grid.autoWidth = false;
40274 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40276 if (config.background) {
40277 // render grid on panel activation (if panel background)
40278 this.on('activate', function(gp) {
40279 if (!gp.grid.rendered) {
40280 gp.grid.render(this.wrapper);
40281 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40286 this.grid.render(this.wrapper);
40287 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40290 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40291 // ??? needed ??? config.el = this.wrapper;
40296 // xtype created footer. - not sure if will work as we normally have to render first..
40297 if (this.footer && !this.footer.el && this.footer.xtype) {
40299 var ctr = this.grid.getView().getFooterPanel(true);
40300 this.footer.dataSource = this.grid.dataSource;
40301 this.footer = Roo.factory(this.footer, Roo);
40302 this.footer.render(ctr);
40312 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40313 getId : function(){
40314 return this.grid.id;
40318 * Returns the grid for this panel
40319 * @return {Roo.bootstrap.Table}
40321 getGrid : function(){
40325 setSize : function(width, height){
40326 if(!this.ignoreResize(width, height)){
40327 var grid = this.grid;
40328 var size = this.adjustForComponents(width, height);
40329 // tfoot is not a footer?
40332 var gridel = grid.getGridEl();
40333 gridel.setSize(size.width, size.height);
40335 var tbd = grid.getGridEl().select('tbody', true).first();
40336 var thd = grid.getGridEl().select('thead',true).first();
40337 var tbf= grid.getGridEl().select('tfoot', true).first();
40340 size.height -= tbf.getHeight();
40343 size.height -= thd.getHeight();
40346 tbd.setSize(size.width, size.height );
40347 // this is for the account management tab -seems to work there.
40348 var thd = grid.getGridEl().select('thead',true).first();
40350 // tbd.setSize(size.width, size.height - thd.getHeight());
40359 beforeSlide : function(){
40360 this.grid.getView().scroller.clip();
40363 afterSlide : function(){
40364 this.grid.getView().scroller.unclip();
40367 destroy : function(){
40368 this.grid.destroy();
40370 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40375 * @class Roo.bootstrap.panel.Nest
40376 * @extends Roo.bootstrap.panel.Content
40378 * Create a new Panel, that can contain a layout.Border.
40381 * @param {Roo.BorderLayout} layout The layout for this panel
40382 * @param {String/Object} config A string to set only the title or a config object
40384 Roo.bootstrap.panel.Nest = function(config)
40386 // construct with only one argument..
40387 /* FIXME - implement nicer consturctors
40388 if (layout.layout) {
40390 layout = config.layout;
40391 delete config.layout;
40393 if (layout.xtype && !layout.getEl) {
40394 // then layout needs constructing..
40395 layout = Roo.factory(layout, Roo);
40399 config.el = config.layout.getEl();
40401 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40403 config.layout.monitorWindowResize = false; // turn off autosizing
40404 this.layout = config.layout;
40405 this.layout.getEl().addClass("roo-layout-nested-layout");
40406 this.layout.parent = this;
40413 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40415 setSize : function(width, height){
40416 if(!this.ignoreResize(width, height)){
40417 var size = this.adjustForComponents(width, height);
40418 var el = this.layout.getEl();
40419 if (size.height < 1) {
40420 el.setWidth(size.width);
40422 el.setSize(size.width, size.height);
40424 var touch = el.dom.offsetWidth;
40425 this.layout.layout();
40426 // ie requires a double layout on the first pass
40427 if(Roo.isIE && !this.initialized){
40428 this.initialized = true;
40429 this.layout.layout();
40434 // activate all subpanels if not currently active..
40436 setActiveState : function(active){
40437 this.active = active;
40438 this.setActiveClass(active);
40441 this.fireEvent("deactivate", this);
40445 this.fireEvent("activate", this);
40446 // not sure if this should happen before or after..
40447 if (!this.layout) {
40448 return; // should not happen..
40451 for (var r in this.layout.regions) {
40452 reg = this.layout.getRegion(r);
40453 if (reg.getActivePanel()) {
40454 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40455 reg.setActivePanel(reg.getActivePanel());
40458 if (!reg.panels.length) {
40461 reg.showPanel(reg.getPanel(0));
40470 * Returns the nested BorderLayout for this panel
40471 * @return {Roo.BorderLayout}
40473 getLayout : function(){
40474 return this.layout;
40478 * Adds a xtype elements to the layout of the nested panel
40482 xtype : 'ContentPanel',
40489 xtype : 'NestedLayoutPanel',
40495 items : [ ... list of content panels or nested layout panels.. ]
40499 * @param {Object} cfg Xtype definition of item to add.
40501 addxtype : function(cfg) {
40502 return this.layout.addxtype(cfg);
40507 * Ext JS Library 1.1.1
40508 * Copyright(c) 2006-2007, Ext JS, LLC.
40510 * Originally Released Under LGPL - original licence link has changed is not relivant.
40513 * <script type="text/javascript">
40516 * @class Roo.TabPanel
40517 * @extends Roo.util.Observable
40518 * A lightweight tab container.
40522 // basic tabs 1, built from existing content
40523 var tabs = new Roo.TabPanel("tabs1");
40524 tabs.addTab("script", "View Script");
40525 tabs.addTab("markup", "View Markup");
40526 tabs.activate("script");
40528 // more advanced tabs, built from javascript
40529 var jtabs = new Roo.TabPanel("jtabs");
40530 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40532 // set up the UpdateManager
40533 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40534 var updater = tab2.getUpdateManager();
40535 updater.setDefaultUrl("ajax1.htm");
40536 tab2.on('activate', updater.refresh, updater, true);
40538 // Use setUrl for Ajax loading
40539 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40540 tab3.setUrl("ajax2.htm", null, true);
40543 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40546 jtabs.activate("jtabs-1");
40549 * Create a new TabPanel.
40550 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40551 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40553 Roo.bootstrap.panel.Tabs = function(config){
40555 * The container element for this TabPanel.
40556 * @type Roo.Element
40558 this.el = Roo.get(config.el);
40561 if(typeof config == "boolean"){
40562 this.tabPosition = config ? "bottom" : "top";
40564 Roo.apply(this, config);
40568 if(this.tabPosition == "bottom"){
40569 // if tabs are at the bottom = create the body first.
40570 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40571 this.el.addClass("roo-tabs-bottom");
40573 // next create the tabs holders
40575 if (this.tabPosition == "west"){
40577 var reg = this.region; // fake it..
40579 if (!reg.mgr.parent) {
40582 reg = reg.mgr.parent.region;
40584 Roo.log("got nest?");
40586 if (reg.mgr.getRegion('west')) {
40587 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40588 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40589 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40590 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40591 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40599 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40600 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40601 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40602 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40607 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40610 // finally - if tabs are at the top, then create the body last..
40611 if(this.tabPosition != "bottom"){
40612 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40613 * @type Roo.Element
40615 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40616 this.el.addClass("roo-tabs-top");
40620 this.bodyEl.setStyle("position", "relative");
40622 this.active = null;
40623 this.activateDelegate = this.activate.createDelegate(this);
40628 * Fires when the active tab changes
40629 * @param {Roo.TabPanel} this
40630 * @param {Roo.TabPanelItem} activePanel The new active tab
40634 * @event beforetabchange
40635 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40636 * @param {Roo.TabPanel} this
40637 * @param {Object} e Set cancel to true on this object to cancel the tab change
40638 * @param {Roo.TabPanelItem} tab The tab being changed to
40640 "beforetabchange" : true
40643 Roo.EventManager.onWindowResize(this.onResize, this);
40644 this.cpad = this.el.getPadding("lr");
40645 this.hiddenCount = 0;
40648 // toolbar on the tabbar support...
40649 if (this.toolbar) {
40650 alert("no toolbar support yet");
40651 this.toolbar = false;
40653 var tcfg = this.toolbar;
40654 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40655 this.toolbar = new Roo.Toolbar(tcfg);
40656 if (Roo.isSafari) {
40657 var tbl = tcfg.container.child('table', true);
40658 tbl.setAttribute('width', '100%');
40666 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40669 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40671 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40673 tabPosition : "top",
40675 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40677 currentTabWidth : 0,
40679 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40683 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40687 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40689 preferredTabWidth : 175,
40691 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40693 resizeTabs : false,
40695 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40697 monitorResize : true,
40699 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40701 toolbar : false, // set by caller..
40703 region : false, /// set by caller
40705 disableTooltips : true, // not used yet...
40708 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40709 * @param {String} id The id of the div to use <b>or create</b>
40710 * @param {String} text The text for the tab
40711 * @param {String} content (optional) Content to put in the TabPanelItem body
40712 * @param {Boolean} closable (optional) True to create a close icon on the tab
40713 * @return {Roo.TabPanelItem} The created TabPanelItem
40715 addTab : function(id, text, content, closable, tpl)
40717 var item = new Roo.bootstrap.panel.TabItem({
40721 closable : closable,
40724 this.addTabItem(item);
40726 item.setContent(content);
40732 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40733 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40734 * @return {Roo.TabPanelItem}
40736 getTab : function(id){
40737 return this.items[id];
40741 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40742 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40744 hideTab : function(id){
40745 var t = this.items[id];
40748 this.hiddenCount++;
40749 this.autoSizeTabs();
40754 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40755 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40757 unhideTab : function(id){
40758 var t = this.items[id];
40760 t.setHidden(false);
40761 this.hiddenCount--;
40762 this.autoSizeTabs();
40767 * Adds an existing {@link Roo.TabPanelItem}.
40768 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40770 addTabItem : function(item)
40772 this.items[item.id] = item;
40773 this.items.push(item);
40774 this.autoSizeTabs();
40775 // if(this.resizeTabs){
40776 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40777 // this.autoSizeTabs();
40779 // item.autoSize();
40784 * Removes a {@link Roo.TabPanelItem}.
40785 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40787 removeTab : function(id){
40788 var items = this.items;
40789 var tab = items[id];
40790 if(!tab) { return; }
40791 var index = items.indexOf(tab);
40792 if(this.active == tab && items.length > 1){
40793 var newTab = this.getNextAvailable(index);
40798 this.stripEl.dom.removeChild(tab.pnode.dom);
40799 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40800 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40802 items.splice(index, 1);
40803 delete this.items[tab.id];
40804 tab.fireEvent("close", tab);
40805 tab.purgeListeners();
40806 this.autoSizeTabs();
40809 getNextAvailable : function(start){
40810 var items = this.items;
40812 // look for a next tab that will slide over to
40813 // replace the one being removed
40814 while(index < items.length){
40815 var item = items[++index];
40816 if(item && !item.isHidden()){
40820 // if one isn't found select the previous tab (on the left)
40823 var item = items[--index];
40824 if(item && !item.isHidden()){
40832 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40833 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40835 disableTab : function(id){
40836 var tab = this.items[id];
40837 if(tab && this.active != tab){
40843 * Enables a {@link Roo.TabPanelItem} that is disabled.
40844 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40846 enableTab : function(id){
40847 var tab = this.items[id];
40852 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40853 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40854 * @return {Roo.TabPanelItem} The TabPanelItem.
40856 activate : function(id)
40858 //Roo.log('activite:' + id);
40860 var tab = this.items[id];
40864 if(tab == this.active || tab.disabled){
40868 this.fireEvent("beforetabchange", this, e, tab);
40869 if(e.cancel !== true && !tab.disabled){
40871 this.active.hide();
40873 this.active = this.items[id];
40874 this.active.show();
40875 this.fireEvent("tabchange", this, this.active);
40881 * Gets the active {@link Roo.TabPanelItem}.
40882 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40884 getActiveTab : function(){
40885 return this.active;
40889 * Updates the tab body element to fit the height of the container element
40890 * for overflow scrolling
40891 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40893 syncHeight : function(targetHeight){
40894 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40895 var bm = this.bodyEl.getMargins();
40896 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40897 this.bodyEl.setHeight(newHeight);
40901 onResize : function(){
40902 if(this.monitorResize){
40903 this.autoSizeTabs();
40908 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40910 beginUpdate : function(){
40911 this.updating = true;
40915 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40917 endUpdate : function(){
40918 this.updating = false;
40919 this.autoSizeTabs();
40923 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40925 autoSizeTabs : function()
40927 var count = this.items.length;
40928 var vcount = count - this.hiddenCount;
40931 this.stripEl.hide();
40933 this.stripEl.show();
40936 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40941 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40942 var availWidth = Math.floor(w / vcount);
40943 var b = this.stripBody;
40944 if(b.getWidth() > w){
40945 var tabs = this.items;
40946 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40947 if(availWidth < this.minTabWidth){
40948 /*if(!this.sleft){ // incomplete scrolling code
40949 this.createScrollButtons();
40952 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40955 if(this.currentTabWidth < this.preferredTabWidth){
40956 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40962 * Returns the number of tabs in this TabPanel.
40965 getCount : function(){
40966 return this.items.length;
40970 * Resizes all the tabs to the passed width
40971 * @param {Number} The new width
40973 setTabWidth : function(width){
40974 this.currentTabWidth = width;
40975 for(var i = 0, len = this.items.length; i < len; i++) {
40976 if(!this.items[i].isHidden()) {
40977 this.items[i].setWidth(width);
40983 * Destroys this TabPanel
40984 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40986 destroy : function(removeEl){
40987 Roo.EventManager.removeResizeListener(this.onResize, this);
40988 for(var i = 0, len = this.items.length; i < len; i++){
40989 this.items[i].purgeListeners();
40991 if(removeEl === true){
40992 this.el.update("");
40997 createStrip : function(container)
40999 var strip = document.createElement("nav");
41000 strip.className = Roo.bootstrap.version == 4 ?
41001 "navbar-light bg-light" :
41002 "navbar navbar-default"; //"x-tabs-wrap";
41003 container.appendChild(strip);
41007 createStripList : function(strip)
41009 // div wrapper for retard IE
41010 // returns the "tr" element.
41011 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41012 //'<div class="x-tabs-strip-wrap">'+
41013 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41014 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41015 return strip.firstChild; //.firstChild.firstChild.firstChild;
41017 createBody : function(container)
41019 var body = document.createElement("div");
41020 Roo.id(body, "tab-body");
41021 //Roo.fly(body).addClass("x-tabs-body");
41022 Roo.fly(body).addClass("tab-content");
41023 container.appendChild(body);
41026 createItemBody :function(bodyEl, id){
41027 var body = Roo.getDom(id);
41029 body = document.createElement("div");
41032 //Roo.fly(body).addClass("x-tabs-item-body");
41033 Roo.fly(body).addClass("tab-pane");
41034 bodyEl.insertBefore(body, bodyEl.firstChild);
41038 createStripElements : function(stripEl, text, closable, tpl)
41040 var td = document.createElement("li"); // was td..
41041 td.className = 'nav-item';
41043 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41046 stripEl.appendChild(td);
41048 td.className = "x-tabs-closable";
41049 if(!this.closeTpl){
41050 this.closeTpl = new Roo.Template(
41051 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41052 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41053 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41056 var el = this.closeTpl.overwrite(td, {"text": text});
41057 var close = el.getElementsByTagName("div")[0];
41058 var inner = el.getElementsByTagName("em")[0];
41059 return {"el": el, "close": close, "inner": inner};
41062 // not sure what this is..
41063 // if(!this.tabTpl){
41064 //this.tabTpl = new Roo.Template(
41065 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41066 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41068 // this.tabTpl = new Roo.Template(
41069 // '<a href="#">' +
41070 // '<span unselectable="on"' +
41071 // (this.disableTooltips ? '' : ' title="{text}"') +
41072 // ' >{text}</span></a>'
41078 var template = tpl || this.tabTpl || false;
41081 template = new Roo.Template(
41082 Roo.bootstrap.version == 4 ?
41084 '<a class="nav-link" href="#" unselectable="on"' +
41085 (this.disableTooltips ? '' : ' title="{text}"') +
41088 '<a class="nav-link" href="#">' +
41089 '<span unselectable="on"' +
41090 (this.disableTooltips ? '' : ' title="{text}"') +
41091 ' >{text}</span></a>'
41096 switch (typeof(template)) {
41100 template = new Roo.Template(template);
41106 var el = template.overwrite(td, {"text": text});
41108 var inner = el.getElementsByTagName("span")[0];
41110 return {"el": el, "inner": inner};
41118 * @class Roo.TabPanelItem
41119 * @extends Roo.util.Observable
41120 * Represents an individual item (tab plus body) in a TabPanel.
41121 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41122 * @param {String} id The id of this TabPanelItem
41123 * @param {String} text The text for the tab of this TabPanelItem
41124 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41126 Roo.bootstrap.panel.TabItem = function(config){
41128 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41129 * @type Roo.TabPanel
41131 this.tabPanel = config.panel;
41133 * The id for this TabPanelItem
41136 this.id = config.id;
41138 this.disabled = false;
41140 this.text = config.text;
41142 this.loaded = false;
41143 this.closable = config.closable;
41146 * The body element for this TabPanelItem.
41147 * @type Roo.Element
41149 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41150 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41151 this.bodyEl.setStyle("display", "block");
41152 this.bodyEl.setStyle("zoom", "1");
41153 //this.hideAction();
41155 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41157 this.el = Roo.get(els.el);
41158 this.inner = Roo.get(els.inner, true);
41159 this.textEl = Roo.bootstrap.version == 4 ?
41160 this.el : Roo.get(this.el.dom.firstChild, true);
41162 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41163 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41166 // this.el.on("mousedown", this.onTabMouseDown, this);
41167 this.el.on("click", this.onTabClick, this);
41169 if(config.closable){
41170 var c = Roo.get(els.close, true);
41171 c.dom.title = this.closeText;
41172 c.addClassOnOver("close-over");
41173 c.on("click", this.closeClick, this);
41179 * Fires when this tab becomes the active tab.
41180 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41181 * @param {Roo.TabPanelItem} this
41185 * @event beforeclose
41186 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41187 * @param {Roo.TabPanelItem} this
41188 * @param {Object} e Set cancel to true on this object to cancel the close.
41190 "beforeclose": true,
41193 * Fires when this tab is closed.
41194 * @param {Roo.TabPanelItem} this
41198 * @event deactivate
41199 * Fires when this tab is no longer the active tab.
41200 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41201 * @param {Roo.TabPanelItem} this
41203 "deactivate" : true
41205 this.hidden = false;
41207 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41210 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41212 purgeListeners : function(){
41213 Roo.util.Observable.prototype.purgeListeners.call(this);
41214 this.el.removeAllListeners();
41217 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41220 this.status_node.addClass("active");
41223 this.tabPanel.stripWrap.repaint();
41225 this.fireEvent("activate", this.tabPanel, this);
41229 * Returns true if this tab is the active tab.
41230 * @return {Boolean}
41232 isActive : function(){
41233 return this.tabPanel.getActiveTab() == this;
41237 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41240 this.status_node.removeClass("active");
41242 this.fireEvent("deactivate", this.tabPanel, this);
41245 hideAction : function(){
41246 this.bodyEl.hide();
41247 this.bodyEl.setStyle("position", "absolute");
41248 this.bodyEl.setLeft("-20000px");
41249 this.bodyEl.setTop("-20000px");
41252 showAction : function(){
41253 this.bodyEl.setStyle("position", "relative");
41254 this.bodyEl.setTop("");
41255 this.bodyEl.setLeft("");
41256 this.bodyEl.show();
41260 * Set the tooltip for the tab.
41261 * @param {String} tooltip The tab's tooltip
41263 setTooltip : function(text){
41264 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41265 this.textEl.dom.qtip = text;
41266 this.textEl.dom.removeAttribute('title');
41268 this.textEl.dom.title = text;
41272 onTabClick : function(e){
41273 e.preventDefault();
41274 this.tabPanel.activate(this.id);
41277 onTabMouseDown : function(e){
41278 e.preventDefault();
41279 this.tabPanel.activate(this.id);
41282 getWidth : function(){
41283 return this.inner.getWidth();
41286 setWidth : function(width){
41287 var iwidth = width - this.linode.getPadding("lr");
41288 this.inner.setWidth(iwidth);
41289 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41290 this.linode.setWidth(width);
41294 * Show or hide the tab
41295 * @param {Boolean} hidden True to hide or false to show.
41297 setHidden : function(hidden){
41298 this.hidden = hidden;
41299 this.linode.setStyle("display", hidden ? "none" : "");
41303 * Returns true if this tab is "hidden"
41304 * @return {Boolean}
41306 isHidden : function(){
41307 return this.hidden;
41311 * Returns the text for this tab
41314 getText : function(){
41318 autoSize : function(){
41319 //this.el.beginMeasure();
41320 this.textEl.setWidth(1);
41322 * #2804 [new] Tabs in Roojs
41323 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41325 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41326 //this.el.endMeasure();
41330 * Sets the text for the tab (Note: this also sets the tooltip text)
41331 * @param {String} text The tab's text and tooltip
41333 setText : function(text){
41335 this.textEl.update(text);
41336 this.setTooltip(text);
41337 //if(!this.tabPanel.resizeTabs){
41338 // this.autoSize();
41342 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41344 activate : function(){
41345 this.tabPanel.activate(this.id);
41349 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41351 disable : function(){
41352 if(this.tabPanel.active != this){
41353 this.disabled = true;
41354 this.status_node.addClass("disabled");
41359 * Enables this TabPanelItem if it was previously disabled.
41361 enable : function(){
41362 this.disabled = false;
41363 this.status_node.removeClass("disabled");
41367 * Sets the content for this TabPanelItem.
41368 * @param {String} content The content
41369 * @param {Boolean} loadScripts true to look for and load scripts
41371 setContent : function(content, loadScripts){
41372 this.bodyEl.update(content, loadScripts);
41376 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41377 * @return {Roo.UpdateManager} The UpdateManager
41379 getUpdateManager : function(){
41380 return this.bodyEl.getUpdateManager();
41384 * Set a URL to be used to load the content for this TabPanelItem.
41385 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41386 * @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)
41387 * @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)
41388 * @return {Roo.UpdateManager} The UpdateManager
41390 setUrl : function(url, params, loadOnce){
41391 if(this.refreshDelegate){
41392 this.un('activate', this.refreshDelegate);
41394 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41395 this.on("activate", this.refreshDelegate);
41396 return this.bodyEl.getUpdateManager();
41400 _handleRefresh : function(url, params, loadOnce){
41401 if(!loadOnce || !this.loaded){
41402 var updater = this.bodyEl.getUpdateManager();
41403 updater.update(url, params, this._setLoaded.createDelegate(this));
41408 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41409 * Will fail silently if the setUrl method has not been called.
41410 * This does not activate the panel, just updates its content.
41412 refresh : function(){
41413 if(this.refreshDelegate){
41414 this.loaded = false;
41415 this.refreshDelegate();
41420 _setLoaded : function(){
41421 this.loaded = true;
41425 closeClick : function(e){
41428 this.fireEvent("beforeclose", this, o);
41429 if(o.cancel !== true){
41430 this.tabPanel.removeTab(this.id);
41434 * The text displayed in the tooltip for the close icon.
41437 closeText : "Close this tab"
41440 * This script refer to:
41441 * Title: International Telephone Input
41442 * Author: Jack O'Connor
41443 * Code version: v12.1.12
41444 * Availability: https://github.com/jackocnr/intl-tel-input.git
41447 Roo.bootstrap.PhoneInputData = function() {
41450 "Afghanistan (افغانستان)",
41455 "Albania (Shqipëri)",
41460 "Algeria (الجزائر)",
41485 "Antigua and Barbuda",
41495 "Armenia (Հայաստան)",
41511 "Austria (Österreich)",
41516 "Azerbaijan (Azərbaycan)",
41526 "Bahrain (البحرين)",
41531 "Bangladesh (বাংলাদেশ)",
41541 "Belarus (Беларусь)",
41546 "Belgium (België)",
41576 "Bosnia and Herzegovina (Босна и Херцеговина)",
41591 "British Indian Ocean Territory",
41596 "British Virgin Islands",
41606 "Bulgaria (България)",
41616 "Burundi (Uburundi)",
41621 "Cambodia (កម្ពុជា)",
41626 "Cameroon (Cameroun)",
41635 ["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"]
41638 "Cape Verde (Kabu Verdi)",
41643 "Caribbean Netherlands",
41654 "Central African Republic (République centrafricaine)",
41674 "Christmas Island",
41680 "Cocos (Keeling) Islands",
41691 "Comoros (جزر القمر)",
41696 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41701 "Congo (Republic) (Congo-Brazzaville)",
41721 "Croatia (Hrvatska)",
41742 "Czech Republic (Česká republika)",
41747 "Denmark (Danmark)",
41762 "Dominican Republic (República Dominicana)",
41766 ["809", "829", "849"]
41784 "Equatorial Guinea (Guinea Ecuatorial)",
41804 "Falkland Islands (Islas Malvinas)",
41809 "Faroe Islands (Føroyar)",
41830 "French Guiana (Guyane française)",
41835 "French Polynesia (Polynésie française)",
41850 "Georgia (საქართველო)",
41855 "Germany (Deutschland)",
41875 "Greenland (Kalaallit Nunaat)",
41912 "Guinea-Bissau (Guiné Bissau)",
41937 "Hungary (Magyarország)",
41942 "Iceland (Ísland)",
41962 "Iraq (العراق)",
41978 "Israel (ישראל)",
42005 "Jordan (الأردن)",
42010 "Kazakhstan (Казахстан)",
42031 "Kuwait (الكويت)",
42036 "Kyrgyzstan (Кыргызстан)",
42046 "Latvia (Latvija)",
42051 "Lebanon (لبنان)",
42066 "Libya (ليبيا)",
42076 "Lithuania (Lietuva)",
42091 "Macedonia (FYROM) (Македонија)",
42096 "Madagascar (Madagasikara)",
42126 "Marshall Islands",
42136 "Mauritania (موريتانيا)",
42141 "Mauritius (Moris)",
42162 "Moldova (Republica Moldova)",
42172 "Mongolia (Монгол)",
42177 "Montenegro (Crna Gora)",
42187 "Morocco (المغرب)",
42193 "Mozambique (Moçambique)",
42198 "Myanmar (Burma) (မြန်မာ)",
42203 "Namibia (Namibië)",
42218 "Netherlands (Nederland)",
42223 "New Caledonia (Nouvelle-Calédonie)",
42258 "North Korea (조선 민주주의 인민 공화국)",
42263 "Northern Mariana Islands",
42279 "Pakistan (پاکستان)",
42289 "Palestine (فلسطين)",
42299 "Papua New Guinea",
42341 "Réunion (La Réunion)",
42347 "Romania (România)",
42363 "Saint Barthélemy",
42374 "Saint Kitts and Nevis",
42384 "Saint Martin (Saint-Martin (partie française))",
42390 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42395 "Saint Vincent and the Grenadines",
42410 "São Tomé and Príncipe (São Tomé e Príncipe)",
42415 "Saudi Arabia (المملكة العربية السعودية)",
42420 "Senegal (Sénégal)",
42450 "Slovakia (Slovensko)",
42455 "Slovenia (Slovenija)",
42465 "Somalia (Soomaaliya)",
42475 "South Korea (대한민국)",
42480 "South Sudan (جنوب السودان)",
42490 "Sri Lanka (ශ්රී ලංකාව)",
42495 "Sudan (السودان)",
42505 "Svalbard and Jan Mayen",
42516 "Sweden (Sverige)",
42521 "Switzerland (Schweiz)",
42526 "Syria (سوريا)",
42571 "Trinidad and Tobago",
42576 "Tunisia (تونس)",
42581 "Turkey (Türkiye)",
42591 "Turks and Caicos Islands",
42601 "U.S. Virgin Islands",
42611 "Ukraine (Україна)",
42616 "United Arab Emirates (الإمارات العربية المتحدة)",
42638 "Uzbekistan (Oʻzbekiston)",
42648 "Vatican City (Città del Vaticano)",
42659 "Vietnam (Việt Nam)",
42664 "Wallis and Futuna (Wallis-et-Futuna)",
42669 "Western Sahara (الصحراء الغربية)",
42675 "Yemen (اليمن)",
42699 * This script refer to:
42700 * Title: International Telephone Input
42701 * Author: Jack O'Connor
42702 * Code version: v12.1.12
42703 * Availability: https://github.com/jackocnr/intl-tel-input.git
42707 * @class Roo.bootstrap.PhoneInput
42708 * @extends Roo.bootstrap.TriggerField
42709 * An input with International dial-code selection
42711 * @cfg {String} defaultDialCode default '+852'
42712 * @cfg {Array} preferedCountries default []
42715 * Create a new PhoneInput.
42716 * @param {Object} config Configuration options
42719 Roo.bootstrap.PhoneInput = function(config) {
42720 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42723 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42725 listWidth: undefined,
42727 selectedClass: 'active',
42729 invalidClass : "has-warning",
42731 validClass: 'has-success',
42733 allowed: '0123456789',
42738 * @cfg {String} defaultDialCode The default dial code when initializing the input
42740 defaultDialCode: '+852',
42743 * @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
42745 preferedCountries: false,
42747 getAutoCreate : function()
42749 var data = Roo.bootstrap.PhoneInputData();
42750 var align = this.labelAlign || this.parentLabelAlign();
42753 this.allCountries = [];
42754 this.dialCodeMapping = [];
42756 for (var i = 0; i < data.length; i++) {
42758 this.allCountries[i] = {
42762 priority: c[3] || 0,
42763 areaCodes: c[4] || null
42765 this.dialCodeMapping[c[2]] = {
42768 priority: c[3] || 0,
42769 areaCodes: c[4] || null
42781 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42782 maxlength: this.max_length,
42783 cls : 'form-control tel-input',
42784 autocomplete: 'new-password'
42787 var hiddenInput = {
42790 cls: 'hidden-tel-input'
42794 hiddenInput.name = this.name;
42797 if (this.disabled) {
42798 input.disabled = true;
42801 var flag_container = {
42818 cls: this.hasFeedback ? 'has-feedback' : '',
42824 cls: 'dial-code-holder',
42831 cls: 'roo-select2-container input-group',
42838 if (this.fieldLabel.length) {
42841 tooltip: 'This field is required'
42847 cls: 'control-label',
42853 html: this.fieldLabel
42856 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42862 if(this.indicatorpos == 'right') {
42863 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42870 if(align == 'left') {
42878 if(this.labelWidth > 12){
42879 label.style = "width: " + this.labelWidth + 'px';
42881 if(this.labelWidth < 13 && this.labelmd == 0){
42882 this.labelmd = this.labelWidth;
42884 if(this.labellg > 0){
42885 label.cls += ' col-lg-' + this.labellg;
42886 input.cls += ' col-lg-' + (12 - this.labellg);
42888 if(this.labelmd > 0){
42889 label.cls += ' col-md-' + this.labelmd;
42890 container.cls += ' col-md-' + (12 - this.labelmd);
42892 if(this.labelsm > 0){
42893 label.cls += ' col-sm-' + this.labelsm;
42894 container.cls += ' col-sm-' + (12 - this.labelsm);
42896 if(this.labelxs > 0){
42897 label.cls += ' col-xs-' + this.labelxs;
42898 container.cls += ' col-xs-' + (12 - this.labelxs);
42908 var settings = this;
42910 ['xs','sm','md','lg'].map(function(size){
42911 if (settings[size]) {
42912 cfg.cls += ' col-' + size + '-' + settings[size];
42916 this.store = new Roo.data.Store({
42917 proxy : new Roo.data.MemoryProxy({}),
42918 reader : new Roo.data.JsonReader({
42929 'name' : 'dialCode',
42933 'name' : 'priority',
42937 'name' : 'areaCodes',
42944 if(!this.preferedCountries) {
42945 this.preferedCountries = [
42952 var p = this.preferedCountries.reverse();
42955 for (var i = 0; i < p.length; i++) {
42956 for (var j = 0; j < this.allCountries.length; j++) {
42957 if(this.allCountries[j].iso2 == p[i]) {
42958 var t = this.allCountries[j];
42959 this.allCountries.splice(j,1);
42960 this.allCountries.unshift(t);
42966 this.store.proxy.data = {
42968 data: this.allCountries
42974 initEvents : function()
42977 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42979 this.indicator = this.indicatorEl();
42980 this.flag = this.flagEl();
42981 this.dialCodeHolder = this.dialCodeHolderEl();
42983 this.trigger = this.el.select('div.flag-box',true).first();
42984 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42989 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42990 _this.list.setWidth(lw);
42993 this.list.on('mouseover', this.onViewOver, this);
42994 this.list.on('mousemove', this.onViewMove, this);
42995 this.inputEl().on("keyup", this.onKeyUp, this);
42996 this.inputEl().on("keypress", this.onKeyPress, this);
42998 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43000 this.view = new Roo.View(this.list, this.tpl, {
43001 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43004 this.view.on('click', this.onViewClick, this);
43005 this.setValue(this.defaultDialCode);
43008 onTriggerClick : function(e)
43010 Roo.log('trigger click');
43015 if(this.isExpanded()){
43017 this.hasFocus = false;
43019 this.store.load({});
43020 this.hasFocus = true;
43025 isExpanded : function()
43027 return this.list.isVisible();
43030 collapse : function()
43032 if(!this.isExpanded()){
43036 Roo.get(document).un('mousedown', this.collapseIf, this);
43037 Roo.get(document).un('mousewheel', this.collapseIf, this);
43038 this.fireEvent('collapse', this);
43042 expand : function()
43046 if(this.isExpanded() || !this.hasFocus){
43050 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43051 this.list.setWidth(lw);
43054 this.restrictHeight();
43056 Roo.get(document).on('mousedown', this.collapseIf, this);
43057 Roo.get(document).on('mousewheel', this.collapseIf, this);
43059 this.fireEvent('expand', this);
43062 restrictHeight : function()
43064 this.list.alignTo(this.inputEl(), this.listAlign);
43065 this.list.alignTo(this.inputEl(), this.listAlign);
43068 onViewOver : function(e, t)
43070 if(this.inKeyMode){
43073 var item = this.view.findItemFromChild(t);
43076 var index = this.view.indexOf(item);
43077 this.select(index, false);
43082 onViewClick : function(view, doFocus, el, e)
43084 var index = this.view.getSelectedIndexes()[0];
43086 var r = this.store.getAt(index);
43089 this.onSelect(r, index);
43091 if(doFocus !== false && !this.blockFocus){
43092 this.inputEl().focus();
43096 onViewMove : function(e, t)
43098 this.inKeyMode = false;
43101 select : function(index, scrollIntoView)
43103 this.selectedIndex = index;
43104 this.view.select(index);
43105 if(scrollIntoView !== false){
43106 var el = this.view.getNode(index);
43108 this.list.scrollChildIntoView(el, false);
43113 createList : function()
43115 this.list = Roo.get(document.body).createChild({
43117 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43118 style: 'display:none'
43121 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43124 collapseIf : function(e)
43126 var in_combo = e.within(this.el);
43127 var in_list = e.within(this.list);
43128 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43130 if (in_combo || in_list || is_list) {
43136 onSelect : function(record, index)
43138 if(this.fireEvent('beforeselect', this, record, index) !== false){
43140 this.setFlagClass(record.data.iso2);
43141 this.setDialCode(record.data.dialCode);
43142 this.hasFocus = false;
43144 this.fireEvent('select', this, record, index);
43148 flagEl : function()
43150 var flag = this.el.select('div.flag',true).first();
43157 dialCodeHolderEl : function()
43159 var d = this.el.select('input.dial-code-holder',true).first();
43166 setDialCode : function(v)
43168 this.dialCodeHolder.dom.value = '+'+v;
43171 setFlagClass : function(n)
43173 this.flag.dom.className = 'flag '+n;
43176 getValue : function()
43178 var v = this.inputEl().getValue();
43179 if(this.dialCodeHolder) {
43180 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43185 setValue : function(v)
43187 var d = this.getDialCode(v);
43189 //invalid dial code
43190 if(v.length == 0 || !d || d.length == 0) {
43192 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43193 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43199 this.setFlagClass(this.dialCodeMapping[d].iso2);
43200 this.setDialCode(d);
43201 this.inputEl().dom.value = v.replace('+'+d,'');
43202 this.hiddenEl().dom.value = this.getValue();
43207 getDialCode : function(v)
43211 if (v.length == 0) {
43212 return this.dialCodeHolder.dom.value;
43216 if (v.charAt(0) != "+") {
43219 var numericChars = "";
43220 for (var i = 1; i < v.length; i++) {
43221 var c = v.charAt(i);
43224 if (this.dialCodeMapping[numericChars]) {
43225 dialCode = v.substr(1, i);
43227 if (numericChars.length == 4) {
43237 this.setValue(this.defaultDialCode);
43241 hiddenEl : function()
43243 return this.el.select('input.hidden-tel-input',true).first();
43246 // after setting val
43247 onKeyUp : function(e){
43248 this.setValue(this.getValue());
43251 onKeyPress : function(e){
43252 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43259 * @class Roo.bootstrap.MoneyField
43260 * @extends Roo.bootstrap.ComboBox
43261 * Bootstrap MoneyField class
43264 * Create a new MoneyField.
43265 * @param {Object} config Configuration options
43268 Roo.bootstrap.MoneyField = function(config) {
43270 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43274 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43277 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43279 allowDecimals : true,
43281 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43283 decimalSeparator : ".",
43285 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43287 decimalPrecision : 0,
43289 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43291 allowNegative : true,
43293 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43297 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43299 minValue : Number.NEGATIVE_INFINITY,
43301 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43303 maxValue : Number.MAX_VALUE,
43305 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43307 minText : "The minimum value for this field is {0}",
43309 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43311 maxText : "The maximum value for this field is {0}",
43313 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43314 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43316 nanText : "{0} is not a valid number",
43318 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43322 * @cfg {String} defaults currency of the MoneyField
43323 * value should be in lkey
43325 defaultCurrency : false,
43327 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43329 thousandsDelimiter : false,
43331 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43342 getAutoCreate : function()
43344 var align = this.labelAlign || this.parentLabelAlign();
43356 cls : 'form-control roo-money-amount-input',
43357 autocomplete: 'new-password'
43360 var hiddenInput = {
43364 cls: 'hidden-number-input'
43367 if(this.max_length) {
43368 input.maxlength = this.max_length;
43372 hiddenInput.name = this.name;
43375 if (this.disabled) {
43376 input.disabled = true;
43379 var clg = 12 - this.inputlg;
43380 var cmd = 12 - this.inputmd;
43381 var csm = 12 - this.inputsm;
43382 var cxs = 12 - this.inputxs;
43386 cls : 'row roo-money-field',
43390 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43394 cls: 'roo-select2-container input-group',
43398 cls : 'form-control roo-money-currency-input',
43399 autocomplete: 'new-password',
43401 name : this.currencyName
43405 cls : 'input-group-addon',
43419 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43423 cls: this.hasFeedback ? 'has-feedback' : '',
43434 if (this.fieldLabel.length) {
43437 tooltip: 'This field is required'
43443 cls: 'control-label',
43449 html: this.fieldLabel
43452 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43458 if(this.indicatorpos == 'right') {
43459 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43466 if(align == 'left') {
43474 if(this.labelWidth > 12){
43475 label.style = "width: " + this.labelWidth + 'px';
43477 if(this.labelWidth < 13 && this.labelmd == 0){
43478 this.labelmd = this.labelWidth;
43480 if(this.labellg > 0){
43481 label.cls += ' col-lg-' + this.labellg;
43482 input.cls += ' col-lg-' + (12 - this.labellg);
43484 if(this.labelmd > 0){
43485 label.cls += ' col-md-' + this.labelmd;
43486 container.cls += ' col-md-' + (12 - this.labelmd);
43488 if(this.labelsm > 0){
43489 label.cls += ' col-sm-' + this.labelsm;
43490 container.cls += ' col-sm-' + (12 - this.labelsm);
43492 if(this.labelxs > 0){
43493 label.cls += ' col-xs-' + this.labelxs;
43494 container.cls += ' col-xs-' + (12 - this.labelxs);
43505 var settings = this;
43507 ['xs','sm','md','lg'].map(function(size){
43508 if (settings[size]) {
43509 cfg.cls += ' col-' + size + '-' + settings[size];
43516 initEvents : function()
43518 this.indicator = this.indicatorEl();
43520 this.initCurrencyEvent();
43522 this.initNumberEvent();
43525 initCurrencyEvent : function()
43528 throw "can not find store for combo";
43531 this.store = Roo.factory(this.store, Roo.data);
43532 this.store.parent = this;
43536 this.triggerEl = this.el.select('.input-group-addon', true).first();
43538 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43543 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43544 _this.list.setWidth(lw);
43547 this.list.on('mouseover', this.onViewOver, this);
43548 this.list.on('mousemove', this.onViewMove, this);
43549 this.list.on('scroll', this.onViewScroll, this);
43552 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43555 this.view = new Roo.View(this.list, this.tpl, {
43556 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43559 this.view.on('click', this.onViewClick, this);
43561 this.store.on('beforeload', this.onBeforeLoad, this);
43562 this.store.on('load', this.onLoad, this);
43563 this.store.on('loadexception', this.onLoadException, this);
43565 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43566 "up" : function(e){
43567 this.inKeyMode = true;
43571 "down" : function(e){
43572 if(!this.isExpanded()){
43573 this.onTriggerClick();
43575 this.inKeyMode = true;
43580 "enter" : function(e){
43583 if(this.fireEvent("specialkey", this, e)){
43584 this.onViewClick(false);
43590 "esc" : function(e){
43594 "tab" : function(e){
43597 if(this.fireEvent("specialkey", this, e)){
43598 this.onViewClick(false);
43606 doRelay : function(foo, bar, hname){
43607 if(hname == 'down' || this.scope.isExpanded()){
43608 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43616 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43620 initNumberEvent : function(e)
43622 this.inputEl().on("keydown" , this.fireKey, this);
43623 this.inputEl().on("focus", this.onFocus, this);
43624 this.inputEl().on("blur", this.onBlur, this);
43626 this.inputEl().relayEvent('keyup', this);
43628 if(this.indicator){
43629 this.indicator.addClass('invisible');
43632 this.originalValue = this.getValue();
43634 if(this.validationEvent == 'keyup'){
43635 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43636 this.inputEl().on('keyup', this.filterValidation, this);
43638 else if(this.validationEvent !== false){
43639 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43642 if(this.selectOnFocus){
43643 this.on("focus", this.preFocus, this);
43646 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43647 this.inputEl().on("keypress", this.filterKeys, this);
43649 this.inputEl().relayEvent('keypress', this);
43652 var allowed = "0123456789";
43654 if(this.allowDecimals){
43655 allowed += this.decimalSeparator;
43658 if(this.allowNegative){
43662 if(this.thousandsDelimiter) {
43666 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43668 var keyPress = function(e){
43670 var k = e.getKey();
43672 var c = e.getCharCode();
43675 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43676 allowed.indexOf(String.fromCharCode(c)) === -1
43682 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43686 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43691 this.inputEl().on("keypress", keyPress, this);
43695 onTriggerClick : function(e)
43702 this.loadNext = false;
43704 if(this.isExpanded()){
43709 this.hasFocus = true;
43711 if(this.triggerAction == 'all') {
43712 this.doQuery(this.allQuery, true);
43716 this.doQuery(this.getRawValue());
43719 getCurrency : function()
43721 var v = this.currencyEl().getValue();
43726 restrictHeight : function()
43728 this.list.alignTo(this.currencyEl(), this.listAlign);
43729 this.list.alignTo(this.currencyEl(), this.listAlign);
43732 onViewClick : function(view, doFocus, el, e)
43734 var index = this.view.getSelectedIndexes()[0];
43736 var r = this.store.getAt(index);
43739 this.onSelect(r, index);
43743 onSelect : function(record, index){
43745 if(this.fireEvent('beforeselect', this, record, index) !== false){
43747 this.setFromCurrencyData(index > -1 ? record.data : false);
43751 this.fireEvent('select', this, record, index);
43755 setFromCurrencyData : function(o)
43759 this.lastCurrency = o;
43761 if (this.currencyField) {
43762 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43764 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43767 this.lastSelectionText = currency;
43769 //setting default currency
43770 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43771 this.setCurrency(this.defaultCurrency);
43775 this.setCurrency(currency);
43778 setFromData : function(o)
43782 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43784 this.setFromCurrencyData(c);
43789 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43791 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43794 this.setValue(value);
43798 setCurrency : function(v)
43800 this.currencyValue = v;
43803 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43808 setValue : function(v)
43810 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43816 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43818 this.inputEl().dom.value = (v == '') ? '' :
43819 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43821 if(!this.allowZero && v === '0') {
43822 this.hiddenEl().dom.value = '';
43823 this.inputEl().dom.value = '';
43830 getRawValue : function()
43832 var v = this.inputEl().getValue();
43837 getValue : function()
43839 return this.fixPrecision(this.parseValue(this.getRawValue()));
43842 parseValue : function(value)
43844 if(this.thousandsDelimiter) {
43846 r = new RegExp(",", "g");
43847 value = value.replace(r, "");
43850 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43851 return isNaN(value) ? '' : value;
43855 fixPrecision : function(value)
43857 if(this.thousandsDelimiter) {
43859 r = new RegExp(",", "g");
43860 value = value.replace(r, "");
43863 var nan = isNaN(value);
43865 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43866 return nan ? '' : value;
43868 return parseFloat(value).toFixed(this.decimalPrecision);
43871 decimalPrecisionFcn : function(v)
43873 return Math.floor(v);
43876 validateValue : function(value)
43878 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43882 var num = this.parseValue(value);
43885 this.markInvalid(String.format(this.nanText, value));
43889 if(num < this.minValue){
43890 this.markInvalid(String.format(this.minText, this.minValue));
43894 if(num > this.maxValue){
43895 this.markInvalid(String.format(this.maxText, this.maxValue));
43902 validate : function()
43904 if(this.disabled || this.allowBlank){
43909 var currency = this.getCurrency();
43911 if(this.validateValue(this.getRawValue()) && currency.length){
43916 this.markInvalid();
43920 getName: function()
43925 beforeBlur : function()
43931 var v = this.parseValue(this.getRawValue());
43938 onBlur : function()
43942 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43943 //this.el.removeClass(this.focusClass);
43946 this.hasFocus = false;
43948 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43952 var v = this.getValue();
43954 if(String(v) !== String(this.startValue)){
43955 this.fireEvent('change', this, v, this.startValue);
43958 this.fireEvent("blur", this);
43961 inputEl : function()
43963 return this.el.select('.roo-money-amount-input', true).first();
43966 currencyEl : function()
43968 return this.el.select('.roo-money-currency-input', true).first();
43971 hiddenEl : function()
43973 return this.el.select('input.hidden-number-input',true).first();
43977 * @class Roo.bootstrap.BezierSignature
43978 * @extends Roo.bootstrap.Component
43979 * Bootstrap BezierSignature class
43980 * This script refer to:
43981 * Title: Signature Pad
43983 * Availability: https://github.com/szimek/signature_pad
43986 * Create a new BezierSignature
43987 * @param {Object} config The config object
43990 Roo.bootstrap.BezierSignature = function(config){
43991 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43997 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44004 mouse_btn_down: true,
44007 * @cfg {int} canvas height
44009 canvas_height: '200px',
44012 * @cfg {float|function} Radius of a single dot.
44017 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44022 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44027 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44032 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44037 * @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.
44039 bg_color: 'rgba(0, 0, 0, 0)',
44042 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44044 dot_color: 'black',
44047 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44049 velocity_filter_weight: 0.7,
44052 * @cfg {function} Callback when stroke begin.
44057 * @cfg {function} Callback when stroke end.
44061 getAutoCreate : function()
44063 var cls = 'roo-signature column';
44066 cls += ' ' + this.cls;
44076 for(var i = 0; i < col_sizes.length; i++) {
44077 if(this[col_sizes[i]]) {
44078 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44088 cls: 'roo-signature-body',
44092 cls: 'roo-signature-body-canvas',
44093 height: this.canvas_height,
44094 width: this.canvas_width
44101 style: 'display: none'
44109 initEvents: function()
44111 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44113 var canvas = this.canvasEl();
44115 // mouse && touch event swapping...
44116 canvas.dom.style.touchAction = 'none';
44117 canvas.dom.style.msTouchAction = 'none';
44119 this.mouse_btn_down = false;
44120 canvas.on('mousedown', this._handleMouseDown, this);
44121 canvas.on('mousemove', this._handleMouseMove, this);
44122 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44124 if (window.PointerEvent) {
44125 canvas.on('pointerdown', this._handleMouseDown, this);
44126 canvas.on('pointermove', this._handleMouseMove, this);
44127 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44130 if ('ontouchstart' in window) {
44131 canvas.on('touchstart', this._handleTouchStart, this);
44132 canvas.on('touchmove', this._handleTouchMove, this);
44133 canvas.on('touchend', this._handleTouchEnd, this);
44136 Roo.EventManager.onWindowResize(this.resize, this, true);
44138 // file input event
44139 this.fileEl().on('change', this.uploadImage, this);
44146 resize: function(){
44148 var canvas = this.canvasEl().dom;
44149 var ctx = this.canvasElCtx();
44150 var img_data = false;
44152 if(canvas.width > 0) {
44153 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44155 // setting canvas width will clean img data
44158 var style = window.getComputedStyle ?
44159 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44161 var padding_left = parseInt(style.paddingLeft) || 0;
44162 var padding_right = parseInt(style.paddingRight) || 0;
44164 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44167 ctx.putImageData(img_data, 0, 0);
44171 _handleMouseDown: function(e)
44173 if (e.browserEvent.which === 1) {
44174 this.mouse_btn_down = true;
44175 this.strokeBegin(e);
44179 _handleMouseMove: function (e)
44181 if (this.mouse_btn_down) {
44182 this.strokeMoveUpdate(e);
44186 _handleMouseUp: function (e)
44188 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44189 this.mouse_btn_down = false;
44194 _handleTouchStart: function (e) {
44196 e.preventDefault();
44197 if (e.browserEvent.targetTouches.length === 1) {
44198 // var touch = e.browserEvent.changedTouches[0];
44199 // this.strokeBegin(touch);
44201 this.strokeBegin(e); // assume e catching the correct xy...
44205 _handleTouchMove: function (e) {
44206 e.preventDefault();
44207 // var touch = event.targetTouches[0];
44208 // _this._strokeMoveUpdate(touch);
44209 this.strokeMoveUpdate(e);
44212 _handleTouchEnd: function (e) {
44213 var wasCanvasTouched = e.target === this.canvasEl().dom;
44214 if (wasCanvasTouched) {
44215 e.preventDefault();
44216 // var touch = event.changedTouches[0];
44217 // _this._strokeEnd(touch);
44222 reset: function () {
44223 this._lastPoints = [];
44224 this._lastVelocity = 0;
44225 this._lastWidth = (this.min_width + this.max_width) / 2;
44226 this.canvasElCtx().fillStyle = this.dot_color;
44229 strokeMoveUpdate: function(e)
44231 this.strokeUpdate(e);
44233 if (this.throttle) {
44234 this.throttleStroke(this.strokeUpdate, this.throttle);
44237 this.strokeUpdate(e);
44241 strokeBegin: function(e)
44243 var newPointGroup = {
44244 color: this.dot_color,
44248 if (typeof this.onBegin === 'function') {
44252 this.curve_data.push(newPointGroup);
44254 this.strokeUpdate(e);
44257 strokeUpdate: function(e)
44259 var rect = this.canvasEl().dom.getBoundingClientRect();
44260 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44261 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44262 var lastPoints = lastPointGroup.points;
44263 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44264 var isLastPointTooClose = lastPoint
44265 ? point.distanceTo(lastPoint) <= this.min_distance
44267 var color = lastPointGroup.color;
44268 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44269 var curve = this.addPoint(point);
44271 this.drawDot({color: color, point: point});
44274 this.drawCurve({color: color, curve: curve});
44284 strokeEnd: function(e)
44286 this.strokeUpdate(e);
44287 if (typeof this.onEnd === 'function') {
44292 addPoint: function (point) {
44293 var _lastPoints = this._lastPoints;
44294 _lastPoints.push(point);
44295 if (_lastPoints.length > 2) {
44296 if (_lastPoints.length === 3) {
44297 _lastPoints.unshift(_lastPoints[0]);
44299 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44300 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44301 _lastPoints.shift();
44307 calculateCurveWidths: function (startPoint, endPoint) {
44308 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44309 (1 - this.velocity_filter_weight) * this._lastVelocity;
44311 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44314 start: this._lastWidth
44317 this._lastVelocity = velocity;
44318 this._lastWidth = newWidth;
44322 drawDot: function (_a) {
44323 var color = _a.color, point = _a.point;
44324 var ctx = this.canvasElCtx();
44325 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44327 this.drawCurveSegment(point.x, point.y, width);
44329 ctx.fillStyle = color;
44333 drawCurve: function (_a) {
44334 var color = _a.color, curve = _a.curve;
44335 var ctx = this.canvasElCtx();
44336 var widthDelta = curve.endWidth - curve.startWidth;
44337 var drawSteps = Math.floor(curve.length()) * 2;
44339 ctx.fillStyle = color;
44340 for (var i = 0; i < drawSteps; i += 1) {
44341 var t = i / drawSteps;
44347 var x = uuu * curve.startPoint.x;
44348 x += 3 * uu * t * curve.control1.x;
44349 x += 3 * u * tt * curve.control2.x;
44350 x += ttt * curve.endPoint.x;
44351 var y = uuu * curve.startPoint.y;
44352 y += 3 * uu * t * curve.control1.y;
44353 y += 3 * u * tt * curve.control2.y;
44354 y += ttt * curve.endPoint.y;
44355 var width = curve.startWidth + ttt * widthDelta;
44356 this.drawCurveSegment(x, y, width);
44362 drawCurveSegment: function (x, y, width) {
44363 var ctx = this.canvasElCtx();
44365 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44366 this.is_empty = false;
44371 var ctx = this.canvasElCtx();
44372 var canvas = this.canvasEl().dom;
44373 ctx.fillStyle = this.bg_color;
44374 ctx.clearRect(0, 0, canvas.width, canvas.height);
44375 ctx.fillRect(0, 0, canvas.width, canvas.height);
44376 this.curve_data = [];
44378 this.is_empty = true;
44383 return this.el.select('input',true).first();
44386 canvasEl: function()
44388 return this.el.select('canvas',true).first();
44391 canvasElCtx: function()
44393 return this.el.select('canvas',true).first().dom.getContext('2d');
44396 getImage: function(type)
44398 if(this.is_empty) {
44403 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44406 drawFromImage: function(img_src)
44408 var img = new Image();
44410 img.onload = function(){
44411 this.canvasElCtx().drawImage(img, 0, 0);
44416 this.is_empty = false;
44419 selectImage: function()
44421 this.fileEl().dom.click();
44424 uploadImage: function(e)
44426 var reader = new FileReader();
44428 reader.onload = function(e){
44429 var img = new Image();
44430 img.onload = function(){
44432 this.canvasElCtx().drawImage(img, 0, 0);
44434 img.src = e.target.result;
44437 reader.readAsDataURL(e.target.files[0]);
44440 // Bezier Point Constructor
44441 Point: (function () {
44442 function Point(x, y, time) {
44445 this.time = time || Date.now();
44447 Point.prototype.distanceTo = function (start) {
44448 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44450 Point.prototype.equals = function (other) {
44451 return this.x === other.x && this.y === other.y && this.time === other.time;
44453 Point.prototype.velocityFrom = function (start) {
44454 return this.time !== start.time
44455 ? this.distanceTo(start) / (this.time - start.time)
44462 // Bezier Constructor
44463 Bezier: (function () {
44464 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44465 this.startPoint = startPoint;
44466 this.control2 = control2;
44467 this.control1 = control1;
44468 this.endPoint = endPoint;
44469 this.startWidth = startWidth;
44470 this.endWidth = endWidth;
44472 Bezier.fromPoints = function (points, widths, scope) {
44473 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44474 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44475 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44477 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44478 var dx1 = s1.x - s2.x;
44479 var dy1 = s1.y - s2.y;
44480 var dx2 = s2.x - s3.x;
44481 var dy2 = s2.y - s3.y;
44482 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44483 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44484 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44485 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44486 var dxm = m1.x - m2.x;
44487 var dym = m1.y - m2.y;
44488 var k = l2 / (l1 + l2);
44489 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44490 var tx = s2.x - cm.x;
44491 var ty = s2.y - cm.y;
44493 c1: new scope.Point(m1.x + tx, m1.y + ty),
44494 c2: new scope.Point(m2.x + tx, m2.y + ty)
44497 Bezier.prototype.length = function () {
44502 for (var i = 0; i <= steps; i += 1) {
44504 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44505 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44507 var xdiff = cx - px;
44508 var ydiff = cy - py;
44509 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44516 Bezier.prototype.point = function (t, start, c1, c2, end) {
44517 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44518 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44519 + (3.0 * c2 * (1.0 - t) * t * t)
44520 + (end * t * t * t);
44525 throttleStroke: function(fn, wait) {
44526 if (wait === void 0) { wait = 250; }
44528 var timeout = null;
44532 var later = function () {
44533 previous = Date.now();
44535 result = fn.apply(storedContext, storedArgs);
44537 storedContext = null;
44541 return function wrapper() {
44543 for (var _i = 0; _i < arguments.length; _i++) {
44544 args[_i] = arguments[_i];
44546 var now = Date.now();
44547 var remaining = wait - (now - previous);
44548 storedContext = this;
44550 if (remaining <= 0 || remaining > wait) {
44552 clearTimeout(timeout);
44556 result = fn.apply(storedContext, storedArgs);
44558 storedContext = null;
44562 else if (!timeout) {
44563 timeout = window.setTimeout(later, remaining);