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, //
2079 layoutCls : function()
2083 Roo.log(this.margin_bottom.length);
2084 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2087 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2090 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2095 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2101 // more generic support?
2109 // Roo.log("Call onRender: " + this.xtype);
2110 /* We are looking at something like this.
2112 <img src="..." class="card-img-top" alt="...">
2113 <div class="card-body">
2114 <h5 class="card-title">Card title</h5>
2115 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2117 >> this bit is really the body...
2118 <div> << we will ad dthis in hopefully it will not break shit.
2120 ** card text does not actually have any styling...
2122 <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>
2125 <a href="#" class="card-link">Card link</a>
2128 <div class="card-footer">
2129 <small class="text-muted">Last updated 3 mins ago</small>
2133 getAutoCreate : function(){
2141 if (this.weight.length && this.weight != 'light') {
2142 cfg.cls += ' text-white';
2144 cfg.cls += ' text-dark'; // need as it's nested..
2146 if (this.weight.length) {
2147 cfg.cls += ' bg-' + this.weight;
2150 cfg.cls += ' ' + this.layoutCls();
2153 var hdr_ctr = false;
2154 if (this.header.length) {
2156 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2165 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2171 if (this.collapsable) {
2174 cls : 'd-block user-select-none',
2178 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2183 hdr.cn.push(hdr_ctr);
2188 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2193 if (this.header_image.length) {
2196 cls : 'card-img-top',
2197 src: this.header_image // escape?
2202 cls : 'card-img-top d-none'
2208 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2212 if (this.collapsable || this.rotateable) {
2215 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2222 if (this.title.length) {
2226 src: this.title // escape?
2230 if (this.subtitle.length) {
2234 src: this.subtitle // escape?
2240 cls : 'roo-card-body-ctr'
2243 if (this.html.length) {
2249 // fixme ? handle objects?
2251 if (this.footer.length) {
2254 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2259 cfg.cn.push({cls : 'card-footer d-none'});
2268 getCardHeader : function()
2270 var ret = this.el.select('.card-header',true).first();
2271 if (ret.hasClass('d-none')) {
2272 ret.removeClass('d-none');
2277 getCardFooter : function()
2279 var ret = this.el.select('.card-footer',true).first();
2280 if (ret.hasClass('d-none')) {
2281 ret.removeClass('d-none');
2286 getCardImageTop : function()
2288 var ret = this.el.select('.card-img-top',true).first();
2289 if (ret.hasClass('d-none')) {
2290 ret.removeClass('d-none');
2296 getChildContainer : function()
2302 return this.el.select('.roo-card-body-ctr',true).first();
2305 initEvents: function()
2307 this.bodyEl = this.el.select('.card-body',true).first();
2308 this.containerEl = this.getChildContainer();
2310 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311 containerScroll: true,
2312 ddGroup: this.drag_group || 'default_card_drag_group'
2314 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2316 if (this.dropable) {
2317 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318 containerScroll: true,
2319 ddGroup: this.drop_group || 'default_card_drag_group'
2321 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2328 if (this.collapsable) {
2329 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2331 if (this.rotateable) {
2332 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2334 this.collapsableEl = this.el.select('.roo-collapsable').first();
2336 this.footerEl = this.el.select('.card-footer').first();
2337 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339 this.headerEl = this.el.select('.card-header',true).first();
2342 this.el.addClass('roo-card-rotated');
2343 this.fireEvent('rotate', this, true);
2347 getDragData : function(e)
2349 var target = this.getEl();
2351 //this.handleSelection(e);
2356 nodes: this.getEl(),
2361 dragData.ddel = target.dom ; // the div element
2362 Roo.log(target.getWidth( ));
2363 dragData.ddel.style.width = target.getWidth() + 'px';
2370 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2371 * whole Element becomes the target, and this causes the drop gesture to append.
2373 getTargetFromEvent : function(e, dragged_card_el)
2375 var target = e.getTarget();
2376 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377 target = target.parentNode;
2388 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389 // see if target is one of the 'cards'...
2392 //Roo.log(this.items.length);
2395 var last_card_n = 0;
2397 for (var i = 0;i< this.items.length;i++) {
2399 if (!this.items[i].el.hasClass('card')) {
2402 pos = this.getDropPoint(e, this.items[i].el.dom);
2404 cards_len = ret.cards.length;
2405 //Roo.log(this.items[i].el.dom.id);
2406 ret.cards.push(this.items[i]);
2408 if (ret.card_n < 0 && pos == 'above') {
2409 ret.position = cards_len > 0 ? 'below' : pos;
2410 ret.items_n = i > 0 ? i - 1 : 0;
2411 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2412 ret.card = ret.cards[ret.card_n];
2415 if (!ret.cards.length) {
2417 ret.position = 'below';
2421 // could not find a card.. stick it at the end..
2422 if (ret.card_n < 0) {
2423 ret.card_n = last_card_n;
2424 ret.card = ret.cards[last_card_n];
2425 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426 ret.position = 'below';
2429 if (this.items[ret.items_n].el == dragged_card_el) {
2433 if (ret.position == 'below') {
2434 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2436 if (card_after && card_after.el == dragged_card_el) {
2443 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2445 if (card_before && card_before.el == dragged_card_el) {
2452 onNodeEnter : function(n, dd, e, data){
2455 onNodeOver : function(n, dd, e, data)
2458 var target_info = this.getTargetFromEvent(e,data.source.el);
2459 if (target_info === false) {
2460 this.dropPlaceHolder('hide');
2463 Roo.log(['getTargetFromEvent', target_info ]);
2466 this.dropPlaceHolder('show', target_info,data);
2470 onNodeOut : function(n, dd, e, data){
2471 this.dropPlaceHolder('hide');
2474 onNodeDrop : function(n, dd, e, data)
2477 // call drop - return false if
2479 // this could actually fail - if the Network drops..
2480 // we will ignore this at present..- client should probably reload
2481 // the whole set of cards if stuff like that fails.
2484 var info = this.getTargetFromEvent(e,data.source.el);
2485 if (info === false) {
2488 this.dropPlaceHolder('hide');
2494 this.acceptCard(data.source, info.position, info.card, info.items_n);
2498 firstChildCard : function()
2500 for (var i = 0;i< this.items.length;i++) {
2502 if (!this.items[i].el.hasClass('card')) {
2505 return this.items[i];
2507 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2512 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2514 acceptCard : function(move_card, position, next_to_card )
2516 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2520 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2522 move_card.parent().removeCard(move_card);
2525 var dom = move_card.el.dom;
2526 dom.style.width = ''; // clear with - which is set by drag.
2528 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529 var cardel = next_to_card.el.dom;
2531 if (position == 'above' ) {
2532 cardel.parentNode.insertBefore(dom, cardel);
2533 } else if (cardel.nextSibling) {
2534 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2536 cardel.parentNode.append(dom);
2539 // card container???
2540 this.containerEl.dom.append(dom);
2543 //FIXME HANDLE card = true
2545 // add this to the correct place in items.
2547 // remove Card from items.
2550 if (this.items.length) {
2552 //Roo.log([info.items_n, info.position, this.items.length]);
2553 for (var i =0; i < this.items.length; i++) {
2554 if (i == to_items_n && position == 'above') {
2555 nitems.push(move_card);
2557 nitems.push(this.items[i]);
2558 if (i == to_items_n && position == 'below') {
2559 nitems.push(move_card);
2562 this.items = nitems;
2563 Roo.log(this.items);
2565 this.items.push(move_card);
2568 move_card.parentId = this.id;
2574 removeCard : function(c)
2576 this.items = this.items.filter(function(e) { return e != c });
2579 dom.parentNode.removeChild(dom);
2580 dom.style.width = ''; // clear with - which is set by drag.
2585 /** Decide whether to drop above or below a View node. */
2586 getDropPoint : function(e, n, dd)
2591 if (n == this.containerEl.dom) {
2594 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595 var c = t + (b - t) / 2;
2596 var y = Roo.lib.Event.getPageY(e);
2603 onToggleCollapse : function(e)
2605 if (this.collapsed) {
2606 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607 this.collapsableEl.addClass('show');
2608 this.collapsed = false;
2611 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612 this.collapsableEl.removeClass('show');
2613 this.collapsed = true;
2618 onToggleRotate : function(e)
2620 this.collapsableEl.removeClass('show');
2621 this.footerEl.removeClass('d-none');
2622 this.el.removeClass('roo-card-rotated');
2623 this.el.removeClass('d-none');
2626 this.collapsableEl.addClass('show');
2627 this.rotated = false;
2628 this.fireEvent('rotate', this, this.rotated);
2631 this.el.addClass('roo-card-rotated');
2632 this.footerEl.addClass('d-none');
2633 this.el.select('.roo-collapsable').removeClass('show');
2635 this.rotated = true;
2636 this.fireEvent('rotate', this, this.rotated);
2640 dropPlaceHolder: function (action, info, data)
2642 if (this.dropEl === false) {
2643 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2647 this.dropEl.removeClass(['d-none', 'd-block']);
2648 if (action == 'hide') {
2650 this.dropEl.addClass('d-none');
2653 // FIXME - info.card == true!!!
2654 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2656 if (info.card !== true) {
2657 var cardel = info.card.el.dom;
2659 if (info.position == 'above') {
2660 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661 } else if (cardel.nextSibling) {
2662 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2664 cardel.parentNode.append(this.dropEl.dom);
2667 // card container???
2668 this.containerEl.dom.append(this.dropEl.dom);
2671 this.dropEl.addClass('d-block roo-card-dropzone');
2673 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2680 setHeaderText: function(html)
2683 if (this.headerContainerEl) {
2684 this.headerContainerEl.dom.innerHTML = html;
2694 * Card header - holder for the card header elements.
2699 * @class Roo.bootstrap.CardHeader
2700 * @extends Roo.bootstrap.Element
2701 * Bootstrap CardHeader class
2703 * Create a new Card Header - that you can embed children into
2704 * @param {Object} config The config object
2707 Roo.bootstrap.CardHeader = function(config){
2708 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2711 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2714 container_method : 'getCardHeader'
2727 * Card footer - holder for the card footer elements.
2732 * @class Roo.bootstrap.CardFooter
2733 * @extends Roo.bootstrap.Element
2734 * Bootstrap CardFooter class
2736 * Create a new Card Footer - that you can embed children into
2737 * @param {Object} config The config object
2740 Roo.bootstrap.CardFooter = function(config){
2741 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2744 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2747 container_method : 'getCardFooter'
2760 * Card header - holder for the card header elements.
2765 * @class Roo.bootstrap.CardImageTop
2766 * @extends Roo.bootstrap.Element
2767 * Bootstrap CardImageTop class
2769 * Create a new Card Image Top container
2770 * @param {Object} config The config object
2773 Roo.bootstrap.CardImageTop = function(config){
2774 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2777 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2780 container_method : 'getCardImageTop'
2798 * @class Roo.bootstrap.Img
2799 * @extends Roo.bootstrap.Component
2800 * Bootstrap Img class
2801 * @cfg {Boolean} imgResponsive false | true
2802 * @cfg {String} border rounded | circle | thumbnail
2803 * @cfg {String} src image source
2804 * @cfg {String} alt image alternative text
2805 * @cfg {String} href a tag href
2806 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2807 * @cfg {String} xsUrl xs image source
2808 * @cfg {String} smUrl sm image source
2809 * @cfg {String} mdUrl md image source
2810 * @cfg {String} lgUrl lg image source
2813 * Create a new Input
2814 * @param {Object} config The config object
2817 Roo.bootstrap.Img = function(config){
2818 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2824 * The img click event for the img.
2825 * @param {Roo.EventObject} e
2831 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2833 imgResponsive: true,
2843 getAutoCreate : function()
2845 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2846 return this.createSingleImg();
2851 cls: 'roo-image-responsive-group',
2856 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2858 if(!_this[size + 'Url']){
2864 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2865 html: _this.html || cfg.html,
2866 src: _this[size + 'Url']
2869 img.cls += ' roo-image-responsive-' + size;
2871 var s = ['xs', 'sm', 'md', 'lg'];
2873 s.splice(s.indexOf(size), 1);
2875 Roo.each(s, function(ss){
2876 img.cls += ' hidden-' + ss;
2879 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2880 cfg.cls += ' img-' + _this.border;
2884 cfg.alt = _this.alt;
2897 a.target = _this.target;
2901 cfg.cn.push((_this.href) ? a : img);
2908 createSingleImg : function()
2912 cls: (this.imgResponsive) ? 'img-responsive' : '',
2914 src : 'about:blank' // just incase src get's set to undefined?!?
2917 cfg.html = this.html || cfg.html;
2919 cfg.src = this.src || cfg.src;
2921 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2922 cfg.cls += ' img-' + this.border;
2939 a.target = this.target;
2944 return (this.href) ? a : cfg;
2947 initEvents: function()
2950 this.el.on('click', this.onClick, this);
2955 onClick : function(e)
2957 Roo.log('img onclick');
2958 this.fireEvent('click', this, e);
2961 * Sets the url of the image - used to update it
2962 * @param {String} url the url of the image
2965 setSrc : function(url)
2969 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2970 this.el.dom.src = url;
2974 this.el.select('img', true).first().dom.src = url;
2990 * @class Roo.bootstrap.Link
2991 * @extends Roo.bootstrap.Component
2992 * Bootstrap Link Class
2993 * @cfg {String} alt image alternative text
2994 * @cfg {String} href a tag href
2995 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2996 * @cfg {String} html the content of the link.
2997 * @cfg {String} anchor name for the anchor link
2998 * @cfg {String} fa - favicon
3000 * @cfg {Boolean} preventDefault (true | false) default false
3004 * Create a new Input
3005 * @param {Object} config The config object
3008 Roo.bootstrap.Link = function(config){
3009 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3015 * The img click event for the img.
3016 * @param {Roo.EventObject} e
3022 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3026 preventDefault: false,
3032 getAutoCreate : function()
3034 var html = this.html || '';
3036 if (this.fa !== false) {
3037 html = '<i class="fa fa-' + this.fa + '"></i>';
3042 // anchor's do not require html/href...
3043 if (this.anchor === false) {
3045 cfg.href = this.href || '#';
3047 cfg.name = this.anchor;
3048 if (this.html !== false || this.fa !== false) {
3051 if (this.href !== false) {
3052 cfg.href = this.href;
3056 if(this.alt !== false){
3061 if(this.target !== false) {
3062 cfg.target = this.target;
3068 initEvents: function() {
3070 if(!this.href || this.preventDefault){
3071 this.el.on('click', this.onClick, this);
3075 onClick : function(e)
3077 if(this.preventDefault){
3080 //Roo.log('img onclick');
3081 this.fireEvent('click', this, e);
3094 * @class Roo.bootstrap.Header
3095 * @extends Roo.bootstrap.Component
3096 * Bootstrap Header class
3097 * @cfg {String} html content of header
3098 * @cfg {Number} level (1|2|3|4|5|6) default 1
3101 * Create a new Header
3102 * @param {Object} config The config object
3106 Roo.bootstrap.Header = function(config){
3107 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3110 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3118 getAutoCreate : function(){
3123 tag: 'h' + (1 *this.level),
3124 html: this.html || ''
3136 * Ext JS Library 1.1.1
3137 * Copyright(c) 2006-2007, Ext JS, LLC.
3139 * Originally Released Under LGPL - original licence link has changed is not relivant.
3142 * <script type="text/javascript">
3146 * @class Roo.bootstrap.MenuMgr
3147 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3150 Roo.bootstrap.MenuMgr = function(){
3151 var menus, active, groups = {}, attached = false, lastShow = new Date();
3153 // private - called when first menu is created
3156 active = new Roo.util.MixedCollection();
3157 Roo.get(document).addKeyListener(27, function(){
3158 if(active.length > 0){
3166 if(active && active.length > 0){
3167 var c = active.clone();
3177 if(active.length < 1){
3178 Roo.get(document).un("mouseup", onMouseDown);
3186 var last = active.last();
3187 lastShow = new Date();
3190 Roo.get(document).on("mouseup", onMouseDown);
3195 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3196 m.parentMenu.activeChild = m;
3197 }else if(last && last.isVisible()){
3198 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3203 function onBeforeHide(m){
3205 m.activeChild.hide();
3207 if(m.autoHideTimer){
3208 clearTimeout(m.autoHideTimer);
3209 delete m.autoHideTimer;
3214 function onBeforeShow(m){
3215 var pm = m.parentMenu;
3216 if(!pm && !m.allowOtherMenus){
3218 }else if(pm && pm.activeChild && active != m){
3219 pm.activeChild.hide();
3223 // private this should really trigger on mouseup..
3224 function onMouseDown(e){
3225 Roo.log("on Mouse Up");
3227 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3228 Roo.log("MenuManager hideAll");
3237 function onBeforeCheck(mi, state){
3239 var g = groups[mi.group];
3240 for(var i = 0, l = g.length; i < l; i++){
3242 g[i].setChecked(false);
3251 * Hides all menus that are currently visible
3253 hideAll : function(){
3258 register : function(menu){
3262 menus[menu.id] = menu;
3263 menu.on("beforehide", onBeforeHide);
3264 menu.on("hide", onHide);
3265 menu.on("beforeshow", onBeforeShow);
3266 menu.on("show", onShow);
3268 if(g && menu.events["checkchange"]){
3272 groups[g].push(menu);
3273 menu.on("checkchange", onCheck);
3278 * Returns a {@link Roo.menu.Menu} object
3279 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3280 * be used to generate and return a new Menu instance.
3282 get : function(menu){
3283 if(typeof menu == "string"){ // menu id
3285 }else if(menu.events){ // menu instance
3288 /*else if(typeof menu.length == 'number'){ // array of menu items?
3289 return new Roo.bootstrap.Menu({items:menu});
3290 }else{ // otherwise, must be a config
3291 return new Roo.bootstrap.Menu(menu);
3298 unregister : function(menu){
3299 delete menus[menu.id];
3300 menu.un("beforehide", onBeforeHide);
3301 menu.un("hide", onHide);
3302 menu.un("beforeshow", onBeforeShow);
3303 menu.un("show", onShow);
3305 if(g && menu.events["checkchange"]){
3306 groups[g].remove(menu);
3307 menu.un("checkchange", onCheck);
3312 registerCheckable : function(menuItem){
3313 var g = menuItem.group;
3318 groups[g].push(menuItem);
3319 menuItem.on("beforecheckchange", onBeforeCheck);
3324 unregisterCheckable : function(menuItem){
3325 var g = menuItem.group;
3327 groups[g].remove(menuItem);
3328 menuItem.un("beforecheckchange", onBeforeCheck);
3340 * @class Roo.bootstrap.Menu
3341 * @extends Roo.bootstrap.Component
3342 * Bootstrap Menu class - container for MenuItems
3343 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3344 * @cfg {bool} hidden if the menu should be hidden when rendered.
3345 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3346 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3350 * @param {Object} config The config object
3354 Roo.bootstrap.Menu = function(config){
3355 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3356 if (this.registerMenu && this.type != 'treeview') {
3357 Roo.bootstrap.MenuMgr.register(this);
3364 * Fires before this menu is displayed (return false to block)
3365 * @param {Roo.menu.Menu} this
3370 * Fires before this menu is hidden (return false to block)
3371 * @param {Roo.menu.Menu} this
3376 * Fires after this menu is displayed
3377 * @param {Roo.menu.Menu} this
3382 * Fires after this menu is hidden
3383 * @param {Roo.menu.Menu} this
3388 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3389 * @param {Roo.menu.Menu} this
3390 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3391 * @param {Roo.EventObject} e
3396 * Fires when the mouse is hovering over this menu
3397 * @param {Roo.menu.Menu} this
3398 * @param {Roo.EventObject} e
3399 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3404 * Fires when the mouse exits this menu
3405 * @param {Roo.menu.Menu} this
3406 * @param {Roo.EventObject} e
3407 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3412 * Fires when a menu item contained in this menu is clicked
3413 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3414 * @param {Roo.EventObject} e
3418 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3421 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3425 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3428 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3430 registerMenu : true,
3432 menuItems :false, // stores the menu items..
3442 getChildContainer : function() {
3446 getAutoCreate : function(){
3448 //if (['right'].indexOf(this.align)!==-1) {
3449 // cfg.cn[1].cls += ' pull-right'
3455 cls : 'dropdown-menu' ,
3456 style : 'z-index:1000'
3460 if (this.type === 'submenu') {
3461 cfg.cls = 'submenu active';
3463 if (this.type === 'treeview') {
3464 cfg.cls = 'treeview-menu';
3469 initEvents : function() {
3471 // Roo.log("ADD event");
3472 // Roo.log(this.triggerEl.dom);
3474 this.triggerEl.on('click', this.onTriggerClick, this);
3476 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3479 if (this.triggerEl.hasClass('nav-item')) {
3480 // dropdown toggle on the 'a' in BS4?
3481 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3483 this.triggerEl.addClass('dropdown-toggle');
3486 this.el.on('touchstart' , this.onTouch, this);
3488 this.el.on('click' , this.onClick, this);
3490 this.el.on("mouseover", this.onMouseOver, this);
3491 this.el.on("mouseout", this.onMouseOut, this);
3495 findTargetItem : function(e)
3497 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3501 //Roo.log(t); Roo.log(t.id);
3503 //Roo.log(this.menuitems);
3504 return this.menuitems.get(t.id);
3506 //return this.items.get(t.menuItemId);
3512 onTouch : function(e)
3514 Roo.log("menu.onTouch");
3515 //e.stopEvent(); this make the user popdown broken
3519 onClick : function(e)
3521 Roo.log("menu.onClick");
3523 var t = this.findTargetItem(e);
3524 if(!t || t.isContainer){
3529 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3530 if(t == this.activeItem && t.shouldDeactivate(e)){
3531 this.activeItem.deactivate();
3532 delete this.activeItem;
3536 this.setActiveItem(t, true);
3544 Roo.log('pass click event');
3548 this.fireEvent("click", this, t, e);
3552 if(!t.href.length || t.href == '#'){
3553 (function() { _this.hide(); }).defer(100);
3558 onMouseOver : function(e){
3559 var t = this.findTargetItem(e);
3562 // if(t.canActivate && !t.disabled){
3563 // this.setActiveItem(t, true);
3567 this.fireEvent("mouseover", this, e, t);
3569 isVisible : function(){
3570 return !this.hidden;
3572 onMouseOut : function(e){
3573 var t = this.findTargetItem(e);
3576 // if(t == this.activeItem && t.shouldDeactivate(e)){
3577 // this.activeItem.deactivate();
3578 // delete this.activeItem;
3581 this.fireEvent("mouseout", this, e, t);
3586 * Displays this menu relative to another element
3587 * @param {String/HTMLElement/Roo.Element} element The element to align to
3588 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3589 * the element (defaults to this.defaultAlign)
3590 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3592 show : function(el, pos, parentMenu)
3594 if (false === this.fireEvent("beforeshow", this)) {
3595 Roo.log("show canceled");
3598 this.parentMenu = parentMenu;
3603 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3606 * Displays this menu at a specific xy position
3607 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3608 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3610 showAt : function(xy, parentMenu, /* private: */_e){
3611 this.parentMenu = parentMenu;
3616 this.fireEvent("beforeshow", this);
3617 //xy = this.el.adjustForConstraints(xy);
3621 this.hideMenuItems();
3622 this.hidden = false;
3623 this.triggerEl.addClass('open');
3624 this.el.addClass('show');
3626 // reassign x when hitting right
3627 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3628 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3631 // reassign y when hitting bottom
3632 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3633 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3636 // but the list may align on trigger left or trigger top... should it be a properity?
3638 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3643 this.fireEvent("show", this);
3649 this.doFocus.defer(50, this);
3653 doFocus : function(){
3655 this.focusEl.focus();
3660 * Hides this menu and optionally all parent menus
3661 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3663 hide : function(deep)
3665 if (false === this.fireEvent("beforehide", this)) {
3666 Roo.log("hide canceled");
3669 this.hideMenuItems();
3670 if(this.el && this.isVisible()){
3672 if(this.activeItem){
3673 this.activeItem.deactivate();
3674 this.activeItem = null;
3676 this.triggerEl.removeClass('open');;
3677 this.el.removeClass('show');
3679 this.fireEvent("hide", this);
3681 if(deep === true && this.parentMenu){
3682 this.parentMenu.hide(true);
3686 onTriggerClick : function(e)
3688 Roo.log('trigger click');
3690 var target = e.getTarget();
3692 Roo.log(target.nodeName.toLowerCase());
3694 if(target.nodeName.toLowerCase() === 'i'){
3700 onTriggerPress : function(e)
3702 Roo.log('trigger press');
3703 //Roo.log(e.getTarget());
3704 // Roo.log(this.triggerEl.dom);
3706 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3707 var pel = Roo.get(e.getTarget());
3708 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3709 Roo.log('is treeview or dropdown?');
3713 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3717 if (this.isVisible()) {
3722 this.show(this.triggerEl, '?', false);
3725 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3732 hideMenuItems : function()
3734 Roo.log("hide Menu Items");
3739 this.el.select('.open',true).each(function(aa) {
3741 aa.removeClass('open');
3745 addxtypeChild : function (tree, cntr) {
3746 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3748 this.menuitems.add(comp);
3760 this.getEl().dom.innerHTML = '';
3761 this.menuitems.clear();
3775 * @class Roo.bootstrap.MenuItem
3776 * @extends Roo.bootstrap.Component
3777 * Bootstrap MenuItem class
3778 * @cfg {String} html the menu label
3779 * @cfg {String} href the link
3780 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3781 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3782 * @cfg {Boolean} active used on sidebars to highlight active itesm
3783 * @cfg {String} fa favicon to show on left of menu item.
3784 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3788 * Create a new MenuItem
3789 * @param {Object} config The config object
3793 Roo.bootstrap.MenuItem = function(config){
3794 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3799 * The raw click event for the entire grid.
3800 * @param {Roo.bootstrap.MenuItem} this
3801 * @param {Roo.EventObject} e
3807 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3811 preventDefault: false,
3812 isContainer : false,
3816 getAutoCreate : function(){
3818 if(this.isContainer){
3821 cls: 'dropdown-menu-item '
3831 cls : 'dropdown-item',
3836 if (this.fa !== false) {
3839 cls : 'fa fa-' + this.fa
3848 cls: 'dropdown-menu-item',
3851 if (this.parent().type == 'treeview') {
3852 cfg.cls = 'treeview-menu';
3855 cfg.cls += ' active';
3860 anc.href = this.href || cfg.cn[0].href ;
3861 ctag.html = this.html || cfg.cn[0].html ;
3865 initEvents: function()
3867 if (this.parent().type == 'treeview') {
3868 this.el.select('a').on('click', this.onClick, this);
3872 this.menu.parentType = this.xtype;
3873 this.menu.triggerEl = this.el;
3874 this.menu = this.addxtype(Roo.apply({}, this.menu));
3878 onClick : function(e)
3880 Roo.log('item on click ');
3882 if(this.preventDefault){
3885 //this.parent().hideMenuItems();
3887 this.fireEvent('click', this, e);
3906 * @class Roo.bootstrap.MenuSeparator
3907 * @extends Roo.bootstrap.Component
3908 * Bootstrap MenuSeparator class
3911 * Create a new MenuItem
3912 * @param {Object} config The config object
3916 Roo.bootstrap.MenuSeparator = function(config){
3917 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3920 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3922 getAutoCreate : function(){
3941 * @class Roo.bootstrap.Modal
3942 * @extends Roo.bootstrap.Component
3943 * Bootstrap Modal class
3944 * @cfg {String} title Title of dialog
3945 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3946 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3947 * @cfg {Boolean} specificTitle default false
3948 * @cfg {Array} buttons Array of buttons or standard button set..
3949 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3950 * @cfg {Boolean} animate default true
3951 * @cfg {Boolean} allow_close default true
3952 * @cfg {Boolean} fitwindow default false
3953 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3954 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3955 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3956 * @cfg {String} size (sm|lg|xl) default empty
3957 * @cfg {Number} max_width set the max width of modal
3958 * @cfg {Boolean} editableTitle can the title be edited
3963 * Create a new Modal Dialog
3964 * @param {Object} config The config object
3967 Roo.bootstrap.Modal = function(config){
3968 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3973 * The raw btnclick event for the button
3974 * @param {Roo.EventObject} e
3979 * Fire when dialog resize
3980 * @param {Roo.bootstrap.Modal} this
3981 * @param {Roo.EventObject} e
3985 * @event titlechanged
3986 * Fire when the editable title has been changed
3987 * @param {Roo.bootstrap.Modal} this
3988 * @param {Roo.EventObject} value
3990 "titlechanged" : true
3993 this.buttons = this.buttons || [];
3996 this.tmpl = Roo.factory(this.tmpl);
4001 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4003 title : 'test dialog',
4013 specificTitle: false,
4015 buttonPosition: 'right',
4037 editableTitle : false,
4039 onRender : function(ct, position)
4041 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4044 var cfg = Roo.apply({}, this.getAutoCreate());
4047 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4049 //if (!cfg.name.length) {
4053 cfg.cls += ' ' + this.cls;
4056 cfg.style = this.style;
4058 this.el = Roo.get(document.body).createChild(cfg, position);
4060 //var type = this.el.dom.type;
4063 if(this.tabIndex !== undefined){
4064 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4067 this.dialogEl = this.el.select('.modal-dialog',true).first();
4068 this.bodyEl = this.el.select('.modal-body',true).first();
4069 this.closeEl = this.el.select('.modal-header .close', true).first();
4070 this.headerEl = this.el.select('.modal-header',true).first();
4071 this.titleEl = this.el.select('.modal-title',true).first();
4072 this.footerEl = this.el.select('.modal-footer',true).first();
4074 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4076 //this.el.addClass("x-dlg-modal");
4078 if (this.buttons.length) {
4079 Roo.each(this.buttons, function(bb) {
4080 var b = Roo.apply({}, bb);
4081 b.xns = b.xns || Roo.bootstrap;
4082 b.xtype = b.xtype || 'Button';
4083 if (typeof(b.listeners) == 'undefined') {
4084 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4087 var btn = Roo.factory(b);
4089 btn.render(this.getButtonContainer());
4093 // render the children.
4096 if(typeof(this.items) != 'undefined'){
4097 var items = this.items;
4100 for(var i =0;i < items.length;i++) {
4101 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4105 this.items = nitems;
4107 // where are these used - they used to be body/close/footer
4111 //this.el.addClass([this.fieldClass, this.cls]);
4115 getAutoCreate : function()
4117 // we will default to modal-body-overflow - might need to remove or make optional later.
4119 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4120 html : this.html || ''
4125 cls : 'modal-title',
4129 if(this.specificTitle){ // WTF is this?
4134 if (this.allow_close && Roo.bootstrap.version == 3) {
4144 if (this.editableTitle) {
4146 cls: 'form-control roo-editable-title d-none',
4152 if (this.allow_close && Roo.bootstrap.version == 4) {
4162 if(this.size.length){
4163 size = 'modal-' + this.size;
4166 var footer = Roo.bootstrap.version == 3 ?
4168 cls : 'modal-footer',
4172 cls: 'btn-' + this.buttonPosition
4177 { // BS4 uses mr-auto on left buttons....
4178 cls : 'modal-footer'
4189 cls: "modal-dialog " + size,
4192 cls : "modal-content",
4195 cls : 'modal-header',
4210 modal.cls += ' fade';
4216 getChildContainer : function() {
4221 getButtonContainer : function() {
4223 return Roo.bootstrap.version == 4 ?
4224 this.el.select('.modal-footer',true).first()
4225 : this.el.select('.modal-footer div',true).first();
4228 initEvents : function()
4230 if (this.allow_close) {
4231 this.closeEl.on('click', this.hide, this);
4233 Roo.EventManager.onWindowResize(this.resize, this, true);
4234 if (this.editableTitle) {
4235 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4236 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4237 this.headerEditEl.on('keyup', function(e) {
4238 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4239 this.toggleHeaderInput(false)
4242 this.headerEditEl.on('blur', function(e) {
4243 this.toggleHeaderInput(false)
4252 this.maskEl.setSize(
4253 Roo.lib.Dom.getViewWidth(true),
4254 Roo.lib.Dom.getViewHeight(true)
4257 if (this.fitwindow) {
4259 this.dialogEl.setStyle( { 'max-width' : '100%' });
4261 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4262 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4267 if(this.max_width !== 0) {
4269 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4272 this.setSize(w, this.height);
4276 if(this.max_height) {
4277 this.setSize(w,Math.min(
4279 Roo.lib.Dom.getViewportHeight(true) - 60
4285 if(!this.fit_content) {
4286 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4290 this.setSize(w, Math.min(
4292 this.headerEl.getHeight() +
4293 this.footerEl.getHeight() +
4294 this.getChildHeight(this.bodyEl.dom.childNodes),
4295 Roo.lib.Dom.getViewportHeight(true) - 60)
4301 setSize : function(w,h)
4312 if (!this.rendered) {
4315 this.toggleHeaderInput(false);
4316 //this.el.setStyle('display', 'block');
4317 this.el.removeClass('hideing');
4318 this.el.dom.style.display='block';
4320 Roo.get(document.body).addClass('modal-open');
4322 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4325 this.el.addClass('show');
4326 this.el.addClass('in');
4329 this.el.addClass('show');
4330 this.el.addClass('in');
4333 // not sure how we can show data in here..
4335 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4338 Roo.get(document.body).addClass("x-body-masked");
4340 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4341 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4342 this.maskEl.dom.style.display = 'block';
4343 this.maskEl.addClass('show');
4348 this.fireEvent('show', this);
4350 // set zindex here - otherwise it appears to be ignored...
4351 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4354 this.items.forEach( function(e) {
4355 e.layout ? e.layout() : false;
4363 if(this.fireEvent("beforehide", this) !== false){
4365 this.maskEl.removeClass('show');
4367 this.maskEl.dom.style.display = '';
4368 Roo.get(document.body).removeClass("x-body-masked");
4369 this.el.removeClass('in');
4370 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4372 if(this.animate){ // why
4373 this.el.addClass('hideing');
4374 this.el.removeClass('show');
4376 if (!this.el.hasClass('hideing')) {
4377 return; // it's been shown again...
4380 this.el.dom.style.display='';
4382 Roo.get(document.body).removeClass('modal-open');
4383 this.el.removeClass('hideing');
4387 this.el.removeClass('show');
4388 this.el.dom.style.display='';
4389 Roo.get(document.body).removeClass('modal-open');
4392 this.fireEvent('hide', this);
4395 isVisible : function()
4398 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4402 addButton : function(str, cb)
4406 var b = Roo.apply({}, { html : str } );
4407 b.xns = b.xns || Roo.bootstrap;
4408 b.xtype = b.xtype || 'Button';
4409 if (typeof(b.listeners) == 'undefined') {
4410 b.listeners = { click : cb.createDelegate(this) };
4413 var btn = Roo.factory(b);
4415 btn.render(this.getButtonContainer());
4421 setDefaultButton : function(btn)
4423 //this.el.select('.modal-footer').()
4426 resizeTo: function(w,h)
4428 this.dialogEl.setWidth(w);
4430 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4432 this.bodyEl.setHeight(h - diff);
4434 this.fireEvent('resize', this);
4437 setContentSize : function(w, h)
4441 onButtonClick: function(btn,e)
4444 this.fireEvent('btnclick', btn.name, e);
4447 * Set the title of the Dialog
4448 * @param {String} str new Title
4450 setTitle: function(str) {
4451 this.titleEl.dom.innerHTML = str;
4455 * Set the body of the Dialog
4456 * @param {String} str new Title
4458 setBody: function(str) {
4459 this.bodyEl.dom.innerHTML = str;
4462 * Set the body of the Dialog using the template
4463 * @param {Obj} data - apply this data to the template and replace the body contents.
4465 applyBody: function(obj)
4468 Roo.log("Error - using apply Body without a template");
4471 this.tmpl.overwrite(this.bodyEl, obj);
4474 getChildHeight : function(child_nodes)
4478 child_nodes.length == 0
4483 var child_height = 0;
4485 for(var i = 0; i < child_nodes.length; i++) {
4488 * for modal with tabs...
4489 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4491 var layout_childs = child_nodes[i].childNodes;
4493 for(var j = 0; j < layout_childs.length; j++) {
4495 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4497 var layout_body_childs = layout_childs[j].childNodes;
4499 for(var k = 0; k < layout_body_childs.length; k++) {
4501 if(layout_body_childs[k].classList.contains('navbar')) {
4502 child_height += layout_body_childs[k].offsetHeight;
4506 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4508 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4510 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4512 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4513 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4528 child_height += child_nodes[i].offsetHeight;
4529 // Roo.log(child_nodes[i].offsetHeight);
4532 return child_height;
4534 toggleHeaderInput : function(is_edit)
4536 if (!this.editableTitle) {
4537 return; // not editable.
4539 if (is_edit && this.is_header_editing) {
4540 return; // already editing..
4544 this.headerEditEl.dom.value = this.title;
4545 this.headerEditEl.removeClass('d-none');
4546 this.headerEditEl.dom.focus();
4547 this.titleEl.addClass('d-none');
4549 this.is_header_editing = true;
4552 // flip back to not editing.
4553 this.title = this.headerEditEl.dom.value;
4554 this.headerEditEl.addClass('d-none');
4555 this.titleEl.removeClass('d-none');
4556 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4557 this.is_header_editing = false;
4558 this.fireEvent('titlechanged', this, this.title);
4567 Roo.apply(Roo.bootstrap.Modal, {
4569 * Button config that displays a single OK button
4578 * Button config that displays Yes and No buttons
4594 * Button config that displays OK and Cancel buttons
4609 * Button config that displays Yes, No and Cancel buttons
4634 * messagebox - can be used as a replace
4638 * @class Roo.MessageBox
4639 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4643 Roo.Msg.alert('Status', 'Changes saved successfully.');
4645 // Prompt for user data:
4646 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4648 // process text value...
4652 // Show a dialog using config options:
4654 title:'Save Changes?',
4655 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4656 buttons: Roo.Msg.YESNOCANCEL,
4663 Roo.bootstrap.MessageBox = function(){
4664 var dlg, opt, mask, waitTimer;
4665 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4666 var buttons, activeTextEl, bwidth;
4670 var handleButton = function(button){
4672 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4676 var handleHide = function(){
4678 dlg.el.removeClass(opt.cls);
4681 // Roo.TaskMgr.stop(waitTimer);
4682 // waitTimer = null;
4687 var updateButtons = function(b){
4690 buttons["ok"].hide();
4691 buttons["cancel"].hide();
4692 buttons["yes"].hide();
4693 buttons["no"].hide();
4694 dlg.footerEl.hide();
4698 dlg.footerEl.show();
4699 for(var k in buttons){
4700 if(typeof buttons[k] != "function"){
4703 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4704 width += buttons[k].el.getWidth()+15;
4714 var handleEsc = function(d, k, e){
4715 if(opt && opt.closable !== false){
4725 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4726 * @return {Roo.BasicDialog} The BasicDialog element
4728 getDialog : function(){
4730 dlg = new Roo.bootstrap.Modal( {
4733 //constraintoviewport:false,
4735 //collapsible : false,
4740 //buttonAlign:"center",
4741 closeClick : function(){
4742 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4745 handleButton("cancel");
4750 dlg.on("hide", handleHide);
4752 //dlg.addKeyListener(27, handleEsc);
4754 this.buttons = buttons;
4755 var bt = this.buttonText;
4756 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4757 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4758 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4759 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4761 bodyEl = dlg.bodyEl.createChild({
4763 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4764 '<textarea class="roo-mb-textarea"></textarea>' +
4765 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4767 msgEl = bodyEl.dom.firstChild;
4768 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4769 textboxEl.enableDisplayMode();
4770 textboxEl.addKeyListener([10,13], function(){
4771 if(dlg.isVisible() && opt && opt.buttons){
4774 }else if(opt.buttons.yes){
4775 handleButton("yes");
4779 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4780 textareaEl.enableDisplayMode();
4781 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4782 progressEl.enableDisplayMode();
4784 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4785 var pf = progressEl.dom.firstChild;
4787 pp = Roo.get(pf.firstChild);
4788 pp.setHeight(pf.offsetHeight);
4796 * Updates the message box body text
4797 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4798 * the XHTML-compliant non-breaking space character '&#160;')
4799 * @return {Roo.MessageBox} This message box
4801 updateText : function(text)
4803 if(!dlg.isVisible() && !opt.width){
4804 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4805 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4807 msgEl.innerHTML = text || ' ';
4809 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4810 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4812 Math.min(opt.width || cw , this.maxWidth),
4813 Math.max(opt.minWidth || this.minWidth, bwidth)
4816 activeTextEl.setWidth(w);
4818 if(dlg.isVisible()){
4819 dlg.fixedcenter = false;
4821 // to big, make it scroll. = But as usual stupid IE does not support
4824 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4825 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4826 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4828 bodyEl.dom.style.height = '';
4829 bodyEl.dom.style.overflowY = '';
4832 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4834 bodyEl.dom.style.overflowX = '';
4837 dlg.setContentSize(w, bodyEl.getHeight());
4838 if(dlg.isVisible()){
4839 dlg.fixedcenter = true;
4845 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4846 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4847 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4848 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4849 * @return {Roo.MessageBox} This message box
4851 updateProgress : function(value, text){
4853 this.updateText(text);
4856 if (pp) { // weird bug on my firefox - for some reason this is not defined
4857 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4858 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4864 * Returns true if the message box is currently displayed
4865 * @return {Boolean} True if the message box is visible, else false
4867 isVisible : function(){
4868 return dlg && dlg.isVisible();
4872 * Hides the message box if it is displayed
4875 if(this.isVisible()){
4881 * Displays a new message box, or reinitializes an existing message box, based on the config options
4882 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4883 * The following config object properties are supported:
4885 Property Type Description
4886 ---------- --------------- ------------------------------------------------------------------------------------
4887 animEl String/Element An id or Element from which the message box should animate as it opens and
4888 closes (defaults to undefined)
4889 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4890 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4891 closable Boolean False to hide the top-right close button (defaults to true). Note that
4892 progress and wait dialogs will ignore this property and always hide the
4893 close button as they can only be closed programmatically.
4894 cls String A custom CSS class to apply to the message box element
4895 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4896 displayed (defaults to 75)
4897 fn Function A callback function to execute after closing the dialog. The arguments to the
4898 function will be btn (the name of the button that was clicked, if applicable,
4899 e.g. "ok"), and text (the value of the active text field, if applicable).
4900 Progress and wait dialogs will ignore this option since they do not respond to
4901 user actions and can only be closed programmatically, so any required function
4902 should be called by the same code after it closes the dialog.
4903 icon String A CSS class that provides a background image to be used as an icon for
4904 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4905 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4906 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4907 modal Boolean False to allow user interaction with the page while the message box is
4908 displayed (defaults to true)
4909 msg String A string that will replace the existing message box body text (defaults
4910 to the XHTML-compliant non-breaking space character ' ')
4911 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4912 progress Boolean True to display a progress bar (defaults to false)
4913 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4914 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4915 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4916 title String The title text
4917 value String The string value to set into the active textbox element if displayed
4918 wait Boolean True to display a progress bar (defaults to false)
4919 width Number The width of the dialog in pixels
4926 msg: 'Please enter your address:',
4928 buttons: Roo.MessageBox.OKCANCEL,
4931 animEl: 'addAddressBtn'
4934 * @param {Object} config Configuration options
4935 * @return {Roo.MessageBox} This message box
4937 show : function(options)
4940 // this causes nightmares if you show one dialog after another
4941 // especially on callbacks..
4943 if(this.isVisible()){
4946 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4947 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4948 Roo.log("New Dialog Message:" + options.msg )
4949 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4950 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4953 var d = this.getDialog();
4955 d.setTitle(opt.title || " ");
4956 d.closeEl.setDisplayed(opt.closable !== false);
4957 activeTextEl = textboxEl;
4958 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4963 textareaEl.setHeight(typeof opt.multiline == "number" ?
4964 opt.multiline : this.defaultTextHeight);
4965 activeTextEl = textareaEl;
4974 progressEl.setDisplayed(opt.progress === true);
4976 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4978 this.updateProgress(0);
4979 activeTextEl.dom.value = opt.value || "";
4981 dlg.setDefaultButton(activeTextEl);
4983 var bs = opt.buttons;
4987 }else if(bs && bs.yes){
4988 db = buttons["yes"];
4990 dlg.setDefaultButton(db);
4992 bwidth = updateButtons(opt.buttons);
4993 this.updateText(opt.msg);
4995 d.el.addClass(opt.cls);
4997 d.proxyDrag = opt.proxyDrag === true;
4998 d.modal = opt.modal !== false;
4999 d.mask = opt.modal !== false ? mask : false;
5001 // force it to the end of the z-index stack so it gets a cursor in FF
5002 document.body.appendChild(dlg.el.dom);
5003 d.animateTarget = null;
5004 d.show(options.animEl);
5010 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5011 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5012 * and closing the message box when the process is complete.
5013 * @param {String} title The title bar text
5014 * @param {String} msg The message box body text
5015 * @return {Roo.MessageBox} This message box
5017 progress : function(title, msg){
5024 minWidth: this.minProgressWidth,
5031 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5032 * If a callback function is passed it will be called after the user clicks the button, and the
5033 * id of the button that was clicked will be passed as the only parameter to the callback
5034 * (could also be the top-right close button).
5035 * @param {String} title The title bar text
5036 * @param {String} msg The message box body text
5037 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5038 * @param {Object} scope (optional) The scope of the callback function
5039 * @return {Roo.MessageBox} This message box
5041 alert : function(title, msg, fn, scope)
5056 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5057 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5058 * You are responsible for closing the message box when the process is complete.
5059 * @param {String} msg The message box body text
5060 * @param {String} title (optional) The title bar text
5061 * @return {Roo.MessageBox} This message box
5063 wait : function(msg, title){
5074 waitTimer = Roo.TaskMgr.start({
5076 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5084 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5085 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5086 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5087 * @param {String} title The title bar text
5088 * @param {String} msg The message box body text
5089 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5090 * @param {Object} scope (optional) The scope of the callback function
5091 * @return {Roo.MessageBox} This message box
5093 confirm : function(title, msg, fn, scope){
5097 buttons: this.YESNO,
5106 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5107 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5108 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5109 * (could also be the top-right close button) and the text that was entered will be passed as the two
5110 * parameters to the callback.
5111 * @param {String} title The title bar text
5112 * @param {String} msg The message box body text
5113 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5114 * @param {Object} scope (optional) The scope of the callback function
5115 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5116 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5117 * @return {Roo.MessageBox} This message box
5119 prompt : function(title, msg, fn, scope, multiline){
5123 buttons: this.OKCANCEL,
5128 multiline: multiline,
5135 * Button config that displays a single OK button
5140 * Button config that displays Yes and No buttons
5143 YESNO : {yes:true, no:true},
5145 * Button config that displays OK and Cancel buttons
5148 OKCANCEL : {ok:true, cancel:true},
5150 * Button config that displays Yes, No and Cancel buttons
5153 YESNOCANCEL : {yes:true, no:true, cancel:true},
5156 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5159 defaultTextHeight : 75,
5161 * The maximum width in pixels of the message box (defaults to 600)
5166 * The minimum width in pixels of the message box (defaults to 100)
5171 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5172 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5175 minProgressWidth : 250,
5177 * An object containing the default button text strings that can be overriden for localized language support.
5178 * Supported properties are: ok, cancel, yes and no.
5179 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5192 * Shorthand for {@link Roo.MessageBox}
5194 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5195 Roo.Msg = Roo.Msg || Roo.MessageBox;
5204 * @class Roo.bootstrap.Navbar
5205 * @extends Roo.bootstrap.Component
5206 * Bootstrap Navbar class
5209 * Create a new Navbar
5210 * @param {Object} config The config object
5214 Roo.bootstrap.Navbar = function(config){
5215 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5219 * @event beforetoggle
5220 * Fire before toggle the menu
5221 * @param {Roo.EventObject} e
5223 "beforetoggle" : true
5227 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5236 getAutoCreate : function(){
5239 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5243 initEvents :function ()
5245 //Roo.log(this.el.select('.navbar-toggle',true));
5246 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5253 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5255 var size = this.el.getSize();
5256 this.maskEl.setSize(size.width, size.height);
5257 this.maskEl.enableDisplayMode("block");
5266 getChildContainer : function()
5268 if (this.el && this.el.select('.collapse').getCount()) {
5269 return this.el.select('.collapse',true).first();
5284 onToggle : function()
5287 if(this.fireEvent('beforetoggle', this) === false){
5290 var ce = this.el.select('.navbar-collapse',true).first();
5292 if (!ce.hasClass('show')) {
5302 * Expand the navbar pulldown
5304 expand : function ()
5307 var ce = this.el.select('.navbar-collapse',true).first();
5308 if (ce.hasClass('collapsing')) {
5311 ce.dom.style.height = '';
5313 ce.addClass('in'); // old...
5314 ce.removeClass('collapse');
5315 ce.addClass('show');
5316 var h = ce.getHeight();
5318 ce.removeClass('show');
5319 // at this point we should be able to see it..
5320 ce.addClass('collapsing');
5322 ce.setHeight(0); // resize it ...
5323 ce.on('transitionend', function() {
5324 //Roo.log('done transition');
5325 ce.removeClass('collapsing');
5326 ce.addClass('show');
5327 ce.removeClass('collapse');
5329 ce.dom.style.height = '';
5330 }, this, { single: true} );
5332 ce.dom.scrollTop = 0;
5335 * Collapse the navbar pulldown
5337 collapse : function()
5339 var ce = this.el.select('.navbar-collapse',true).first();
5341 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5342 // it's collapsed or collapsing..
5345 ce.removeClass('in'); // old...
5346 ce.setHeight(ce.getHeight());
5347 ce.removeClass('show');
5348 ce.addClass('collapsing');
5350 ce.on('transitionend', function() {
5351 ce.dom.style.height = '';
5352 ce.removeClass('collapsing');
5353 ce.addClass('collapse');
5354 }, this, { single: true} );
5374 * @class Roo.bootstrap.NavSimplebar
5375 * @extends Roo.bootstrap.Navbar
5376 * Bootstrap Sidebar class
5378 * @cfg {Boolean} inverse is inverted color
5380 * @cfg {String} type (nav | pills | tabs)
5381 * @cfg {Boolean} arrangement stacked | justified
5382 * @cfg {String} align (left | right) alignment
5384 * @cfg {Boolean} main (true|false) main nav bar? default false
5385 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5387 * @cfg {String} tag (header|footer|nav|div) default is nav
5389 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5393 * Create a new Sidebar
5394 * @param {Object} config The config object
5398 Roo.bootstrap.NavSimplebar = function(config){
5399 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5402 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5418 getAutoCreate : function(){
5422 tag : this.tag || 'div',
5423 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5425 if (['light','white'].indexOf(this.weight) > -1) {
5426 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5428 cfg.cls += ' bg-' + this.weight;
5431 cfg.cls += ' navbar-inverse';
5435 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5437 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5446 cls: 'nav nav-' + this.xtype,
5452 this.type = this.type || 'nav';
5453 if (['tabs','pills'].indexOf(this.type) != -1) {
5454 cfg.cn[0].cls += ' nav-' + this.type
5458 if (this.type!=='nav') {
5459 Roo.log('nav type must be nav/tabs/pills')
5461 cfg.cn[0].cls += ' navbar-nav'
5467 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5468 cfg.cn[0].cls += ' nav-' + this.arrangement;
5472 if (this.align === 'right') {
5473 cfg.cn[0].cls += ' navbar-right';
5498 * navbar-expand-md fixed-top
5502 * @class Roo.bootstrap.NavHeaderbar
5503 * @extends Roo.bootstrap.NavSimplebar
5504 * Bootstrap Sidebar class
5506 * @cfg {String} brand what is brand
5507 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5508 * @cfg {String} brand_href href of the brand
5509 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5510 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5511 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5512 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5515 * Create a new Sidebar
5516 * @param {Object} config The config object
5520 Roo.bootstrap.NavHeaderbar = function(config){
5521 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5525 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5532 desktopCenter : false,
5535 getAutoCreate : function(){
5538 tag: this.nav || 'nav',
5539 cls: 'navbar navbar-expand-md',
5545 if (this.desktopCenter) {
5546 cn.push({cls : 'container', cn : []});
5554 cls: 'navbar-toggle navbar-toggler',
5555 'data-toggle': 'collapse',
5560 html: 'Toggle navigation'
5564 cls: 'icon-bar navbar-toggler-icon'
5577 cn.push( Roo.bootstrap.version == 4 ? btn : {
5579 cls: 'navbar-header',
5588 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5592 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5594 if (['light','white'].indexOf(this.weight) > -1) {
5595 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5597 cfg.cls += ' bg-' + this.weight;
5600 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5601 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5603 // tag can override this..
5605 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5608 if (this.brand !== '') {
5609 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5610 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5612 href: this.brand_href ? this.brand_href : '#',
5613 cls: 'navbar-brand',
5621 cfg.cls += ' main-nav';
5629 getHeaderChildContainer : function()
5631 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5632 return this.el.select('.navbar-header',true).first();
5635 return this.getChildContainer();
5638 getChildContainer : function()
5641 return this.el.select('.roo-navbar-collapse',true).first();
5646 initEvents : function()
5648 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5650 if (this.autohide) {
5655 Roo.get(document).on('scroll',function(e) {
5656 var ns = Roo.get(document).getScroll().top;
5657 var os = prevScroll;
5661 ft.removeClass('slideDown');
5662 ft.addClass('slideUp');
5665 ft.removeClass('slideUp');
5666 ft.addClass('slideDown');
5687 * @class Roo.bootstrap.NavSidebar
5688 * @extends Roo.bootstrap.Navbar
5689 * Bootstrap Sidebar class
5692 * Create a new Sidebar
5693 * @param {Object} config The config object
5697 Roo.bootstrap.NavSidebar = function(config){
5698 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5701 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5703 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5705 getAutoCreate : function(){
5710 cls: 'sidebar sidebar-nav'
5732 * @class Roo.bootstrap.NavGroup
5733 * @extends Roo.bootstrap.Component
5734 * Bootstrap NavGroup class
5735 * @cfg {String} align (left|right)
5736 * @cfg {Boolean} inverse
5737 * @cfg {String} type (nav|pills|tab) default nav
5738 * @cfg {String} navId - reference Id for navbar.
5739 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5742 * Create a new nav group
5743 * @param {Object} config The config object
5746 Roo.bootstrap.NavGroup = function(config){
5747 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5750 Roo.bootstrap.NavGroup.register(this);
5754 * Fires when the active item changes
5755 * @param {Roo.bootstrap.NavGroup} this
5756 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5757 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5764 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5776 getAutoCreate : function()
5778 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5784 if (Roo.bootstrap.version == 4) {
5785 if (['tabs','pills'].indexOf(this.type) != -1) {
5786 cfg.cls += ' nav-' + this.type;
5788 // trying to remove so header bar can right align top?
5789 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5790 // do not use on header bar...
5791 cfg.cls += ' navbar-nav';
5796 if (['tabs','pills'].indexOf(this.type) != -1) {
5797 cfg.cls += ' nav-' + this.type
5799 if (this.type !== 'nav') {
5800 Roo.log('nav type must be nav/tabs/pills')
5802 cfg.cls += ' navbar-nav'
5806 if (this.parent() && this.parent().sidebar) {
5809 cls: 'dashboard-menu sidebar-menu'
5815 if (this.form === true) {
5818 cls: 'navbar-form form-inline'
5820 //nav navbar-right ml-md-auto
5821 if (this.align === 'right') {
5822 cfg.cls += ' navbar-right ml-md-auto';
5824 cfg.cls += ' navbar-left';
5828 if (this.align === 'right') {
5829 cfg.cls += ' navbar-right ml-md-auto';
5831 cfg.cls += ' mr-auto';
5835 cfg.cls += ' navbar-inverse';
5843 * sets the active Navigation item
5844 * @param {Roo.bootstrap.NavItem} the new current navitem
5846 setActiveItem : function(item)
5849 Roo.each(this.navItems, function(v){
5854 v.setActive(false, true);
5861 item.setActive(true, true);
5862 this.fireEvent('changed', this, item, prev);
5867 * gets the active Navigation item
5868 * @return {Roo.bootstrap.NavItem} the current navitem
5870 getActive : function()
5874 Roo.each(this.navItems, function(v){
5885 indexOfNav : function()
5889 Roo.each(this.navItems, function(v,i){
5900 * adds a Navigation item
5901 * @param {Roo.bootstrap.NavItem} the navitem to add
5903 addItem : function(cfg)
5905 if (this.form && Roo.bootstrap.version == 4) {
5908 var cn = new Roo.bootstrap.NavItem(cfg);
5910 cn.parentId = this.id;
5911 cn.onRender(this.el, null);
5915 * register a Navigation item
5916 * @param {Roo.bootstrap.NavItem} the navitem to add
5918 register : function(item)
5920 this.navItems.push( item);
5921 item.navId = this.navId;
5926 * clear all the Navigation item
5929 clearAll : function()
5932 this.el.dom.innerHTML = '';
5935 getNavItem: function(tabId)
5938 Roo.each(this.navItems, function(e) {
5939 if (e.tabId == tabId) {
5949 setActiveNext : function()
5951 var i = this.indexOfNav(this.getActive());
5952 if (i > this.navItems.length) {
5955 this.setActiveItem(this.navItems[i+1]);
5957 setActivePrev : function()
5959 var i = this.indexOfNav(this.getActive());
5963 this.setActiveItem(this.navItems[i-1]);
5965 clearWasActive : function(except) {
5966 Roo.each(this.navItems, function(e) {
5967 if (e.tabId != except.tabId && e.was_active) {
5968 e.was_active = false;
5975 getWasActive : function ()
5978 Roo.each(this.navItems, function(e) {
5993 Roo.apply(Roo.bootstrap.NavGroup, {
5997 * register a Navigation Group
5998 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6000 register : function(navgrp)
6002 this.groups[navgrp.navId] = navgrp;
6006 * fetch a Navigation Group based on the navigation ID
6007 * @param {string} the navgroup to add
6008 * @returns {Roo.bootstrap.NavGroup} the navgroup
6010 get: function(navId) {
6011 if (typeof(this.groups[navId]) == 'undefined') {
6013 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6015 return this.groups[navId] ;
6030 * @class Roo.bootstrap.NavItem
6031 * @extends Roo.bootstrap.Component
6032 * Bootstrap Navbar.NavItem class
6033 * @cfg {String} href link to
6034 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6035 * @cfg {Boolean} button_outline show and outlined button
6036 * @cfg {String} html content of button
6037 * @cfg {String} badge text inside badge
6038 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6039 * @cfg {String} glyphicon DEPRICATED - use fa
6040 * @cfg {String} icon DEPRICATED - use fa
6041 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6042 * @cfg {Boolean} active Is item active
6043 * @cfg {Boolean} disabled Is item disabled
6044 * @cfg {String} linkcls Link Class
6045 * @cfg {Boolean} preventDefault (true | false) default false
6046 * @cfg {String} tabId the tab that this item activates.
6047 * @cfg {String} tagtype (a|span) render as a href or span?
6048 * @cfg {Boolean} animateRef (true|false) link to element default false
6051 * Create a new Navbar Item
6052 * @param {Object} config The config object
6054 Roo.bootstrap.NavItem = function(config){
6055 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6060 * The raw click event for the entire grid.
6061 * @param {Roo.EventObject} e
6066 * Fires when the active item active state changes
6067 * @param {Roo.bootstrap.NavItem} this
6068 * @param {boolean} state the new state
6074 * Fires when scroll to element
6075 * @param {Roo.bootstrap.NavItem} this
6076 * @param {Object} options
6077 * @param {Roo.EventObject} e
6085 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6094 preventDefault : false,
6102 button_outline : false,
6106 getAutoCreate : function(){
6113 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6116 cfg.cls += ' active' ;
6118 if (this.disabled) {
6119 cfg.cls += ' disabled';
6123 if (this.button_weight.length) {
6124 cfg.tag = this.href ? 'a' : 'button';
6125 cfg.html = this.html || '';
6126 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6128 cfg.href = this.href;
6131 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6134 // menu .. should add dropdown-menu class - so no need for carat..
6136 if (this.badge !== '') {
6138 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6143 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6147 href : this.href || "#",
6148 html: this.html || ''
6151 if (this.tagtype == 'a') {
6152 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6156 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6159 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6161 if(this.glyphicon) {
6162 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6167 cfg.cn[0].html += " <span class='caret'></span>";
6171 if (this.badge !== '') {
6173 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6181 onRender : function(ct, position)
6183 // Roo.log("Call onRender: " + this.xtype);
6184 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6188 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6189 this.navLink = this.el.select('.nav-link',true).first();
6194 initEvents: function()
6196 if (typeof (this.menu) != 'undefined') {
6197 this.menu.parentType = this.xtype;
6198 this.menu.triggerEl = this.el;
6199 this.menu = this.addxtype(Roo.apply({}, this.menu));
6202 this.el.on('click', this.onClick, this);
6204 //if(this.tagtype == 'span'){
6205 // this.el.select('span',true).on('click', this.onClick, this);
6208 // at this point parent should be available..
6209 this.parent().register(this);
6212 onClick : function(e)
6214 if (e.getTarget('.dropdown-menu-item')) {
6215 // did you click on a menu itemm.... - then don't trigger onclick..
6220 this.preventDefault ||
6223 Roo.log("NavItem - prevent Default?");
6227 if (this.disabled) {
6231 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6232 if (tg && tg.transition) {
6233 Roo.log("waiting for the transitionend");
6239 //Roo.log("fire event clicked");
6240 if(this.fireEvent('click', this, e) === false){
6244 if(this.tagtype == 'span'){
6248 //Roo.log(this.href);
6249 var ael = this.el.select('a',true).first();
6252 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6253 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6254 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6255 return; // ignore... - it's a 'hash' to another page.
6257 Roo.log("NavItem - prevent Default?");
6259 this.scrollToElement(e);
6263 var p = this.parent();
6265 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6266 if (typeof(p.setActiveItem) !== 'undefined') {
6267 p.setActiveItem(this);
6271 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6272 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6273 // remove the collapsed menu expand...
6274 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6278 isActive: function () {
6281 setActive : function(state, fire, is_was_active)
6283 if (this.active && !state && this.navId) {
6284 this.was_active = true;
6285 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6287 nv.clearWasActive(this);
6291 this.active = state;
6294 this.el.removeClass('active');
6295 this.navLink ? this.navLink.removeClass('active') : false;
6296 } else if (!this.el.hasClass('active')) {
6298 this.el.addClass('active');
6299 if (Roo.bootstrap.version == 4 && this.navLink ) {
6300 this.navLink.addClass('active');
6305 this.fireEvent('changed', this, state);
6308 // show a panel if it's registered and related..
6310 if (!this.navId || !this.tabId || !state || is_was_active) {
6314 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6318 var pan = tg.getPanelByName(this.tabId);
6322 // if we can not flip to new panel - go back to old nav highlight..
6323 if (false == tg.showPanel(pan)) {
6324 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6326 var onav = nv.getWasActive();
6328 onav.setActive(true, false, true);
6337 // this should not be here...
6338 setDisabled : function(state)
6340 this.disabled = state;
6342 this.el.removeClass('disabled');
6343 } else if (!this.el.hasClass('disabled')) {
6344 this.el.addClass('disabled');
6350 * Fetch the element to display the tooltip on.
6351 * @return {Roo.Element} defaults to this.el
6353 tooltipEl : function()
6355 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6358 scrollToElement : function(e)
6360 var c = document.body;
6363 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6365 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6366 c = document.documentElement;
6369 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6375 var o = target.calcOffsetsTo(c);
6382 this.fireEvent('scrollto', this, options, e);
6384 Roo.get(c).scrollTo('top', options.value, true);
6397 * <span> icon </span>
6398 * <span> text </span>
6399 * <span>badge </span>
6403 * @class Roo.bootstrap.NavSidebarItem
6404 * @extends Roo.bootstrap.NavItem
6405 * Bootstrap Navbar.NavSidebarItem class
6406 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6407 * {Boolean} open is the menu open
6408 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6409 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6410 * {String} buttonSize (sm|md|lg)the extra classes for the button
6411 * {Boolean} showArrow show arrow next to the text (default true)
6413 * Create a new Navbar Button
6414 * @param {Object} config The config object
6416 Roo.bootstrap.NavSidebarItem = function(config){
6417 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6422 * The raw click event for the entire grid.
6423 * @param {Roo.EventObject} e
6428 * Fires when the active item active state changes
6429 * @param {Roo.bootstrap.NavSidebarItem} this
6430 * @param {boolean} state the new state
6438 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6440 badgeWeight : 'default',
6446 buttonWeight : 'default',
6452 getAutoCreate : function(){
6457 href : this.href || '#',
6463 if(this.buttonView){
6466 href : this.href || '#',
6467 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6480 cfg.cls += ' active';
6483 if (this.disabled) {
6484 cfg.cls += ' disabled';
6487 cfg.cls += ' open x-open';
6490 if (this.glyphicon || this.icon) {
6491 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6492 a.cn.push({ tag : 'i', cls : c }) ;
6495 if(!this.buttonView){
6498 html : this.html || ''
6505 if (this.badge !== '') {
6506 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6512 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6515 a.cls += ' dropdown-toggle treeview' ;
6521 initEvents : function()
6523 if (typeof (this.menu) != 'undefined') {
6524 this.menu.parentType = this.xtype;
6525 this.menu.triggerEl = this.el;
6526 this.menu = this.addxtype(Roo.apply({}, this.menu));
6529 this.el.on('click', this.onClick, this);
6531 if(this.badge !== ''){
6532 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6537 onClick : function(e)
6544 if(this.preventDefault){
6548 this.fireEvent('click', this, e);
6551 disable : function()
6553 this.setDisabled(true);
6558 this.setDisabled(false);
6561 setDisabled : function(state)
6563 if(this.disabled == state){
6567 this.disabled = state;
6570 this.el.addClass('disabled');
6574 this.el.removeClass('disabled');
6579 setActive : function(state)
6581 if(this.active == state){
6585 this.active = state;
6588 this.el.addClass('active');
6592 this.el.removeClass('active');
6597 isActive: function ()
6602 setBadge : function(str)
6608 this.badgeEl.dom.innerHTML = str;
6623 Roo.namespace('Roo.bootstrap.breadcrumb');
6627 * @class Roo.bootstrap.breadcrumb.Nav
6628 * @extends Roo.bootstrap.Component
6629 * Bootstrap Breadcrumb Nav Class
6631 * @children Roo.bootstrap.breadcrumb.Item
6634 * Create a new breadcrumb.Nav
6635 * @param {Object} config The config object
6639 Roo.bootstrap.breadcrumb.Nav = function(config){
6640 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6645 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6647 getAutoCreate : function()
6664 initEvents: function()
6666 this.olEl = this.el.select('ol',true).first();
6668 getChildContainer : function()
6684 * @class Roo.bootstrap.breadcrumb.Nav
6685 * @extends Roo.bootstrap.Component
6686 * Bootstrap Breadcrumb Nav Class
6688 * @children Roo.bootstrap.breadcrumb.Component
6689 * @cfg {String} html the content of the link.
6690 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6691 * @cfg {Boolean} active is it active
6695 * Create a new breadcrumb.Nav
6696 * @param {Object} config The config object
6699 Roo.bootstrap.breadcrumb.Item = function(config){
6700 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6705 * The img click event for the img.
6706 * @param {Roo.EventObject} e
6713 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6718 getAutoCreate : function()
6723 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6725 if (this.href !== false) {
6732 cfg.html = this.html;
6738 initEvents: function()
6741 this.el.select('a', true).first().on('click',this.onClick, this)
6745 onClick : function(e)
6748 this.fireEvent('click',this, e);
6761 * @class Roo.bootstrap.Row
6762 * @extends Roo.bootstrap.Component
6763 * Bootstrap Row class (contains columns...)
6767 * @param {Object} config The config object
6770 Roo.bootstrap.Row = function(config){
6771 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6774 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6776 getAutoCreate : function(){
6795 * @class Roo.bootstrap.Pagination
6796 * @extends Roo.bootstrap.Component
6797 * Bootstrap Pagination class
6798 * @cfg {String} size xs | sm | md | lg
6799 * @cfg {Boolean} inverse false | true
6802 * Create a new Pagination
6803 * @param {Object} config The config object
6806 Roo.bootstrap.Pagination = function(config){
6807 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6810 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6816 getAutoCreate : function(){
6822 cfg.cls += ' inverse';
6828 cfg.cls += " " + this.cls;
6846 * @class Roo.bootstrap.PaginationItem
6847 * @extends Roo.bootstrap.Component
6848 * Bootstrap PaginationItem class
6849 * @cfg {String} html text
6850 * @cfg {String} href the link
6851 * @cfg {Boolean} preventDefault (true | false) default true
6852 * @cfg {Boolean} active (true | false) default false
6853 * @cfg {Boolean} disabled default false
6857 * Create a new PaginationItem
6858 * @param {Object} config The config object
6862 Roo.bootstrap.PaginationItem = function(config){
6863 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6868 * The raw click event for the entire grid.
6869 * @param {Roo.EventObject} e
6875 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6879 preventDefault: true,
6884 getAutoCreate : function(){
6890 href : this.href ? this.href : '#',
6891 html : this.html ? this.html : ''
6901 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6905 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6911 initEvents: function() {
6913 this.el.on('click', this.onClick, this);
6916 onClick : function(e)
6918 Roo.log('PaginationItem on click ');
6919 if(this.preventDefault){
6927 this.fireEvent('click', this, e);
6943 * @class Roo.bootstrap.Slider
6944 * @extends Roo.bootstrap.Component
6945 * Bootstrap Slider class
6948 * Create a new Slider
6949 * @param {Object} config The config object
6952 Roo.bootstrap.Slider = function(config){
6953 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6956 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6958 getAutoCreate : function(){
6962 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6966 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6978 * Ext JS Library 1.1.1
6979 * Copyright(c) 2006-2007, Ext JS, LLC.
6981 * Originally Released Under LGPL - original licence link has changed is not relivant.
6984 * <script type="text/javascript">
6989 * @class Roo.grid.ColumnModel
6990 * @extends Roo.util.Observable
6991 * This is the default implementation of a ColumnModel used by the Grid. It defines
6992 * the columns in the grid.
6995 var colModel = new Roo.grid.ColumnModel([
6996 {header: "Ticker", width: 60, sortable: true, locked: true},
6997 {header: "Company Name", width: 150, sortable: true},
6998 {header: "Market Cap.", width: 100, sortable: true},
6999 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7000 {header: "Employees", width: 100, sortable: true, resizable: false}
7005 * The config options listed for this class are options which may appear in each
7006 * individual column definition.
7007 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7009 * @param {Object} config An Array of column config objects. See this class's
7010 * config objects for details.
7012 Roo.grid.ColumnModel = function(config){
7014 * The config passed into the constructor
7016 this.config = config;
7019 // if no id, create one
7020 // if the column does not have a dataIndex mapping,
7021 // map it to the order it is in the config
7022 for(var i = 0, len = config.length; i < len; i++){
7024 if(typeof c.dataIndex == "undefined"){
7027 if(typeof c.renderer == "string"){
7028 c.renderer = Roo.util.Format[c.renderer];
7030 if(typeof c.id == "undefined"){
7033 if(c.editor && c.editor.xtype){
7034 c.editor = Roo.factory(c.editor, Roo.grid);
7036 if(c.editor && c.editor.isFormField){
7037 c.editor = new Roo.grid.GridEditor(c.editor);
7039 this.lookup[c.id] = c;
7043 * The width of columns which have no width specified (defaults to 100)
7046 this.defaultWidth = 100;
7049 * Default sortable of columns which have no sortable specified (defaults to false)
7052 this.defaultSortable = false;
7056 * @event widthchange
7057 * Fires when the width of a column changes.
7058 * @param {ColumnModel} this
7059 * @param {Number} columnIndex The column index
7060 * @param {Number} newWidth The new width
7062 "widthchange": true,
7064 * @event headerchange
7065 * Fires when the text of a header changes.
7066 * @param {ColumnModel} this
7067 * @param {Number} columnIndex The column index
7068 * @param {Number} newText The new header text
7070 "headerchange": true,
7072 * @event hiddenchange
7073 * Fires when a column is hidden or "unhidden".
7074 * @param {ColumnModel} this
7075 * @param {Number} columnIndex The column index
7076 * @param {Boolean} hidden true if hidden, false otherwise
7078 "hiddenchange": true,
7080 * @event columnmoved
7081 * Fires when a column is moved.
7082 * @param {ColumnModel} this
7083 * @param {Number} oldIndex
7084 * @param {Number} newIndex
7086 "columnmoved" : true,
7088 * @event columlockchange
7089 * Fires when a column's locked state is changed
7090 * @param {ColumnModel} this
7091 * @param {Number} colIndex
7092 * @param {Boolean} locked true if locked
7094 "columnlockchange" : true
7096 Roo.grid.ColumnModel.superclass.constructor.call(this);
7098 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7100 * @cfg {String} header The header text to display in the Grid view.
7103 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7104 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7105 * specified, the column's index is used as an index into the Record's data Array.
7108 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7109 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7112 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7113 * Defaults to the value of the {@link #defaultSortable} property.
7114 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7117 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7120 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7123 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7126 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7129 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7130 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7131 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7132 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7135 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7138 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7141 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7144 * @cfg {String} cursor (Optional)
7147 * @cfg {String} tooltip (Optional)
7150 * @cfg {Number} xs (Optional)
7153 * @cfg {Number} sm (Optional)
7156 * @cfg {Number} md (Optional)
7159 * @cfg {Number} lg (Optional)
7162 * Returns the id of the column at the specified index.
7163 * @param {Number} index The column index
7164 * @return {String} the id
7166 getColumnId : function(index){
7167 return this.config[index].id;
7171 * Returns the column for a specified id.
7172 * @param {String} id The column id
7173 * @return {Object} the column
7175 getColumnById : function(id){
7176 return this.lookup[id];
7181 * Returns the column for a specified dataIndex.
7182 * @param {String} dataIndex The column dataIndex
7183 * @return {Object|Boolean} the column or false if not found
7185 getColumnByDataIndex: function(dataIndex){
7186 var index = this.findColumnIndex(dataIndex);
7187 return index > -1 ? this.config[index] : false;
7191 * Returns the index for a specified column id.
7192 * @param {String} id The column id
7193 * @return {Number} the index, or -1 if not found
7195 getIndexById : function(id){
7196 for(var i = 0, len = this.config.length; i < len; i++){
7197 if(this.config[i].id == id){
7205 * Returns the index for a specified column dataIndex.
7206 * @param {String} dataIndex The column dataIndex
7207 * @return {Number} the index, or -1 if not found
7210 findColumnIndex : function(dataIndex){
7211 for(var i = 0, len = this.config.length; i < len; i++){
7212 if(this.config[i].dataIndex == dataIndex){
7220 moveColumn : function(oldIndex, newIndex){
7221 var c = this.config[oldIndex];
7222 this.config.splice(oldIndex, 1);
7223 this.config.splice(newIndex, 0, c);
7224 this.dataMap = null;
7225 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7228 isLocked : function(colIndex){
7229 return this.config[colIndex].locked === true;
7232 setLocked : function(colIndex, value, suppressEvent){
7233 if(this.isLocked(colIndex) == value){
7236 this.config[colIndex].locked = value;
7238 this.fireEvent("columnlockchange", this, colIndex, value);
7242 getTotalLockedWidth : function(){
7244 for(var i = 0; i < this.config.length; i++){
7245 if(this.isLocked(i) && !this.isHidden(i)){
7246 this.totalWidth += this.getColumnWidth(i);
7252 getLockedCount : function(){
7253 for(var i = 0, len = this.config.length; i < len; i++){
7254 if(!this.isLocked(i)){
7259 return this.config.length;
7263 * Returns the number of columns.
7266 getColumnCount : function(visibleOnly){
7267 if(visibleOnly === true){
7269 for(var i = 0, len = this.config.length; i < len; i++){
7270 if(!this.isHidden(i)){
7276 return this.config.length;
7280 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7281 * @param {Function} fn
7282 * @param {Object} scope (optional)
7283 * @return {Array} result
7285 getColumnsBy : function(fn, scope){
7287 for(var i = 0, len = this.config.length; i < len; i++){
7288 var c = this.config[i];
7289 if(fn.call(scope||this, c, i) === true){
7297 * Returns true if the specified column is sortable.
7298 * @param {Number} col The column index
7301 isSortable : function(col){
7302 if(typeof this.config[col].sortable == "undefined"){
7303 return this.defaultSortable;
7305 return this.config[col].sortable;
7309 * Returns the rendering (formatting) function defined for the column.
7310 * @param {Number} col The column index.
7311 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7313 getRenderer : function(col){
7314 if(!this.config[col].renderer){
7315 return Roo.grid.ColumnModel.defaultRenderer;
7317 return this.config[col].renderer;
7321 * Sets the rendering (formatting) function for a column.
7322 * @param {Number} col The column index
7323 * @param {Function} fn The function to use to process the cell's raw data
7324 * to return HTML markup for the grid view. The render function is called with
7325 * the following parameters:<ul>
7326 * <li>Data value.</li>
7327 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7328 * <li>css A CSS style string to apply to the table cell.</li>
7329 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7330 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7331 * <li>Row index</li>
7332 * <li>Column index</li>
7333 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7335 setRenderer : function(col, fn){
7336 this.config[col].renderer = fn;
7340 * Returns the width for the specified column.
7341 * @param {Number} col The column index
7344 getColumnWidth : function(col){
7345 return this.config[col].width * 1 || this.defaultWidth;
7349 * Sets the width for a column.
7350 * @param {Number} col The column index
7351 * @param {Number} width The new width
7353 setColumnWidth : function(col, width, suppressEvent){
7354 this.config[col].width = width;
7355 this.totalWidth = null;
7357 this.fireEvent("widthchange", this, col, width);
7362 * Returns the total width of all columns.
7363 * @param {Boolean} includeHidden True to include hidden column widths
7366 getTotalWidth : function(includeHidden){
7367 if(!this.totalWidth){
7368 this.totalWidth = 0;
7369 for(var i = 0, len = this.config.length; i < len; i++){
7370 if(includeHidden || !this.isHidden(i)){
7371 this.totalWidth += this.getColumnWidth(i);
7375 return this.totalWidth;
7379 * Returns the header for the specified column.
7380 * @param {Number} col The column index
7383 getColumnHeader : function(col){
7384 return this.config[col].header;
7388 * Sets the header for a column.
7389 * @param {Number} col The column index
7390 * @param {String} header The new header
7392 setColumnHeader : function(col, header){
7393 this.config[col].header = header;
7394 this.fireEvent("headerchange", this, col, header);
7398 * Returns the tooltip for the specified column.
7399 * @param {Number} col The column index
7402 getColumnTooltip : function(col){
7403 return this.config[col].tooltip;
7406 * Sets the tooltip for a column.
7407 * @param {Number} col The column index
7408 * @param {String} tooltip The new tooltip
7410 setColumnTooltip : function(col, tooltip){
7411 this.config[col].tooltip = tooltip;
7415 * Returns the dataIndex for the specified column.
7416 * @param {Number} col The column index
7419 getDataIndex : function(col){
7420 return this.config[col].dataIndex;
7424 * Sets the dataIndex for a column.
7425 * @param {Number} col The column index
7426 * @param {Number} dataIndex The new dataIndex
7428 setDataIndex : function(col, dataIndex){
7429 this.config[col].dataIndex = dataIndex;
7435 * Returns true if the cell is editable.
7436 * @param {Number} colIndex The column index
7437 * @param {Number} rowIndex The row index - this is nto actually used..?
7440 isCellEditable : function(colIndex, rowIndex){
7441 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7445 * Returns the editor defined for the cell/column.
7446 * return false or null to disable editing.
7447 * @param {Number} colIndex The column index
7448 * @param {Number} rowIndex The row index
7451 getCellEditor : function(colIndex, rowIndex){
7452 return this.config[colIndex].editor;
7456 * Sets if a column is editable.
7457 * @param {Number} col The column index
7458 * @param {Boolean} editable True if the column is editable
7460 setEditable : function(col, editable){
7461 this.config[col].editable = editable;
7466 * Returns true if the column is hidden.
7467 * @param {Number} colIndex The column index
7470 isHidden : function(colIndex){
7471 return this.config[colIndex].hidden;
7476 * Returns true if the column width cannot be changed
7478 isFixed : function(colIndex){
7479 return this.config[colIndex].fixed;
7483 * Returns true if the column can be resized
7486 isResizable : function(colIndex){
7487 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7490 * Sets if a column is hidden.
7491 * @param {Number} colIndex The column index
7492 * @param {Boolean} hidden True if the column is hidden
7494 setHidden : function(colIndex, hidden){
7495 this.config[colIndex].hidden = hidden;
7496 this.totalWidth = null;
7497 this.fireEvent("hiddenchange", this, colIndex, hidden);
7501 * Sets the editor for a column.
7502 * @param {Number} col The column index
7503 * @param {Object} editor The editor object
7505 setEditor : function(col, editor){
7506 this.config[col].editor = editor;
7510 Roo.grid.ColumnModel.defaultRenderer = function(value)
7512 if(typeof value == "object") {
7515 if(typeof value == "string" && value.length < 1){
7519 return String.format("{0}", value);
7522 // Alias for backwards compatibility
7523 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7526 * Ext JS Library 1.1.1
7527 * Copyright(c) 2006-2007, Ext JS, LLC.
7529 * Originally Released Under LGPL - original licence link has changed is not relivant.
7532 * <script type="text/javascript">
7536 * @class Roo.LoadMask
7537 * A simple utility class for generically masking elements while loading data. If the element being masked has
7538 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7539 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7540 * element's UpdateManager load indicator and will be destroyed after the initial load.
7542 * Create a new LoadMask
7543 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7544 * @param {Object} config The config object
7546 Roo.LoadMask = function(el, config){
7547 this.el = Roo.get(el);
7548 Roo.apply(this, config);
7550 this.store.on('beforeload', this.onBeforeLoad, this);
7551 this.store.on('load', this.onLoad, this);
7552 this.store.on('loadexception', this.onLoadException, this);
7553 this.removeMask = false;
7555 var um = this.el.getUpdateManager();
7556 um.showLoadIndicator = false; // disable the default indicator
7557 um.on('beforeupdate', this.onBeforeLoad, this);
7558 um.on('update', this.onLoad, this);
7559 um.on('failure', this.onLoad, this);
7560 this.removeMask = true;
7564 Roo.LoadMask.prototype = {
7566 * @cfg {Boolean} removeMask
7567 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7568 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7572 * The text to display in a centered loading message box (defaults to 'Loading...')
7576 * @cfg {String} msgCls
7577 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7579 msgCls : 'x-mask-loading',
7582 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7588 * Disables the mask to prevent it from being displayed
7590 disable : function(){
7591 this.disabled = true;
7595 * Enables the mask so that it can be displayed
7597 enable : function(){
7598 this.disabled = false;
7601 onLoadException : function()
7605 if (typeof(arguments[3]) != 'undefined') {
7606 Roo.MessageBox.alert("Error loading",arguments[3]);
7610 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7611 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7618 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7623 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7627 onBeforeLoad : function(){
7629 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7634 destroy : function(){
7636 this.store.un('beforeload', this.onBeforeLoad, this);
7637 this.store.un('load', this.onLoad, this);
7638 this.store.un('loadexception', this.onLoadException, this);
7640 var um = this.el.getUpdateManager();
7641 um.un('beforeupdate', this.onBeforeLoad, this);
7642 um.un('update', this.onLoad, this);
7643 um.un('failure', this.onLoad, this);
7654 * @class Roo.bootstrap.Table
7655 * @extends Roo.bootstrap.Component
7656 * Bootstrap Table class
7657 * @cfg {String} cls table class
7658 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7659 * @cfg {String} bgcolor Specifies the background color for a table
7660 * @cfg {Number} border Specifies whether the table cells should have borders or not
7661 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7662 * @cfg {Number} cellspacing Specifies the space between cells
7663 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7664 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7665 * @cfg {String} sortable Specifies that the table should be sortable
7666 * @cfg {String} summary Specifies a summary of the content of a table
7667 * @cfg {Number} width Specifies the width of a table
7668 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7670 * @cfg {boolean} striped Should the rows be alternative striped
7671 * @cfg {boolean} bordered Add borders to the table
7672 * @cfg {boolean} hover Add hover highlighting
7673 * @cfg {boolean} condensed Format condensed
7674 * @cfg {boolean} responsive Format condensed
7675 * @cfg {Boolean} loadMask (true|false) default false
7676 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7677 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7678 * @cfg {Boolean} rowSelection (true|false) default false
7679 * @cfg {Boolean} cellSelection (true|false) default false
7680 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7681 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7682 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7683 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7687 * Create a new Table
7688 * @param {Object} config The config object
7691 Roo.bootstrap.Table = function(config){
7692 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7697 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7698 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7699 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7700 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7702 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7704 this.sm.grid = this;
7705 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7706 this.sm = this.selModel;
7707 this.sm.xmodule = this.xmodule || false;
7710 if (this.cm && typeof(this.cm.config) == 'undefined') {
7711 this.colModel = new Roo.grid.ColumnModel(this.cm);
7712 this.cm = this.colModel;
7713 this.cm.xmodule = this.xmodule || false;
7716 this.store= Roo.factory(this.store, Roo.data);
7717 this.ds = this.store;
7718 this.ds.xmodule = this.xmodule || false;
7721 if (this.footer && this.store) {
7722 this.footer.dataSource = this.ds;
7723 this.footer = Roo.factory(this.footer);
7730 * Fires when a cell is clicked
7731 * @param {Roo.bootstrap.Table} this
7732 * @param {Roo.Element} el
7733 * @param {Number} rowIndex
7734 * @param {Number} columnIndex
7735 * @param {Roo.EventObject} e
7739 * @event celldblclick
7740 * Fires when a cell is double clicked
7741 * @param {Roo.bootstrap.Table} this
7742 * @param {Roo.Element} el
7743 * @param {Number} rowIndex
7744 * @param {Number} columnIndex
7745 * @param {Roo.EventObject} e
7747 "celldblclick" : true,
7750 * Fires when a row is clicked
7751 * @param {Roo.bootstrap.Table} this
7752 * @param {Roo.Element} el
7753 * @param {Number} rowIndex
7754 * @param {Roo.EventObject} e
7758 * @event rowdblclick
7759 * Fires when a row is double clicked
7760 * @param {Roo.bootstrap.Table} this
7761 * @param {Roo.Element} el
7762 * @param {Number} rowIndex
7763 * @param {Roo.EventObject} e
7765 "rowdblclick" : true,
7768 * Fires when a mouseover occur
7769 * @param {Roo.bootstrap.Table} this
7770 * @param {Roo.Element} el
7771 * @param {Number} rowIndex
7772 * @param {Number} columnIndex
7773 * @param {Roo.EventObject} e
7778 * Fires when a mouseout occur
7779 * @param {Roo.bootstrap.Table} this
7780 * @param {Roo.Element} el
7781 * @param {Number} rowIndex
7782 * @param {Number} columnIndex
7783 * @param {Roo.EventObject} e
7788 * Fires when a row is rendered, so you can change add a style to it.
7789 * @param {Roo.bootstrap.Table} this
7790 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7794 * @event rowsrendered
7795 * Fires when all the rows have been rendered
7796 * @param {Roo.bootstrap.Table} this
7798 'rowsrendered' : true,
7800 * @event contextmenu
7801 * The raw contextmenu event for the entire grid.
7802 * @param {Roo.EventObject} e
7804 "contextmenu" : true,
7806 * @event rowcontextmenu
7807 * Fires when a row is right clicked
7808 * @param {Roo.bootstrap.Table} this
7809 * @param {Number} rowIndex
7810 * @param {Roo.EventObject} e
7812 "rowcontextmenu" : true,
7814 * @event cellcontextmenu
7815 * Fires when a cell is right clicked
7816 * @param {Roo.bootstrap.Table} this
7817 * @param {Number} rowIndex
7818 * @param {Number} cellIndex
7819 * @param {Roo.EventObject} e
7821 "cellcontextmenu" : true,
7823 * @event headercontextmenu
7824 * Fires when a header is right clicked
7825 * @param {Roo.bootstrap.Table} this
7826 * @param {Number} columnIndex
7827 * @param {Roo.EventObject} e
7829 "headercontextmenu" : true
7833 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7859 rowSelection : false,
7860 cellSelection : false,
7863 // Roo.Element - the tbody
7865 // Roo.Element - thead element
7868 container: false, // used by gridpanel...
7874 auto_hide_footer : false,
7876 getAutoCreate : function()
7878 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7885 if (this.scrollBody) {
7886 cfg.cls += ' table-body-fixed';
7889 cfg.cls += ' table-striped';
7893 cfg.cls += ' table-hover';
7895 if (this.bordered) {
7896 cfg.cls += ' table-bordered';
7898 if (this.condensed) {
7899 cfg.cls += ' table-condensed';
7901 if (this.responsive) {
7902 cfg.cls += ' table-responsive';
7906 cfg.cls+= ' ' +this.cls;
7909 // this lot should be simplifed...
7922 ].forEach(function(k) {
7930 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7933 if(this.store || this.cm){
7934 if(this.headerShow){
7935 cfg.cn.push(this.renderHeader());
7938 cfg.cn.push(this.renderBody());
7940 if(this.footerShow){
7941 cfg.cn.push(this.renderFooter());
7943 // where does this come from?
7944 //cfg.cls+= ' TableGrid';
7947 return { cn : [ cfg ] };
7950 initEvents : function()
7952 if(!this.store || !this.cm){
7955 if (this.selModel) {
7956 this.selModel.initEvents();
7960 //Roo.log('initEvents with ds!!!!');
7962 this.mainBody = this.el.select('tbody', true).first();
7963 this.mainHead = this.el.select('thead', true).first();
7964 this.mainFoot = this.el.select('tfoot', true).first();
7970 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7971 e.on('click', _this.sort, _this);
7974 this.mainBody.on("click", this.onClick, this);
7975 this.mainBody.on("dblclick", this.onDblClick, this);
7977 // why is this done????? = it breaks dialogs??
7978 //this.parent().el.setStyle('position', 'relative');
7982 this.footer.parentId = this.id;
7983 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7986 this.el.select('tfoot tr td').first().addClass('hide');
7991 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7994 this.store.on('load', this.onLoad, this);
7995 this.store.on('beforeload', this.onBeforeLoad, this);
7996 this.store.on('update', this.onUpdate, this);
7997 this.store.on('add', this.onAdd, this);
7998 this.store.on("clear", this.clear, this);
8000 this.el.on("contextmenu", this.onContextMenu, this);
8002 this.mainBody.on('scroll', this.onBodyScroll, this);
8004 this.cm.on("headerchange", this.onHeaderChange, this);
8006 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8010 onContextMenu : function(e, t)
8012 this.processEvent("contextmenu", e);
8015 processEvent : function(name, e)
8017 if (name != 'touchstart' ) {
8018 this.fireEvent(name, e);
8021 var t = e.getTarget();
8023 var cell = Roo.get(t);
8029 if(cell.findParent('tfoot', false, true)){
8033 if(cell.findParent('thead', false, true)){
8035 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8036 cell = Roo.get(t).findParent('th', false, true);
8038 Roo.log("failed to find th in thead?");
8039 Roo.log(e.getTarget());
8044 var cellIndex = cell.dom.cellIndex;
8046 var ename = name == 'touchstart' ? 'click' : name;
8047 this.fireEvent("header" + ename, this, cellIndex, e);
8052 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8053 cell = Roo.get(t).findParent('td', false, true);
8055 Roo.log("failed to find th in tbody?");
8056 Roo.log(e.getTarget());
8061 var row = cell.findParent('tr', false, true);
8062 var cellIndex = cell.dom.cellIndex;
8063 var rowIndex = row.dom.rowIndex - 1;
8067 this.fireEvent("row" + name, this, rowIndex, e);
8071 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8077 onMouseover : function(e, el)
8079 var cell = Roo.get(el);
8085 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8086 cell = cell.findParent('td', false, true);
8089 var row = cell.findParent('tr', false, true);
8090 var cellIndex = cell.dom.cellIndex;
8091 var rowIndex = row.dom.rowIndex - 1; // start from 0
8093 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8097 onMouseout : 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('mouseout', this, cell, rowIndex, cellIndex, e);
8117 onClick : function(e, el)
8119 var cell = Roo.get(el);
8121 if(!cell || (!this.cellSelection && !this.rowSelection)){
8125 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8126 cell = cell.findParent('td', false, true);
8129 if(!cell || typeof(cell) == 'undefined'){
8133 var row = cell.findParent('tr', false, true);
8135 if(!row || typeof(row) == 'undefined'){
8139 var cellIndex = cell.dom.cellIndex;
8140 var rowIndex = this.getRowIndex(row);
8142 // why??? - should these not be based on SelectionModel?
8143 if(this.cellSelection){
8144 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8147 if(this.rowSelection){
8148 this.fireEvent('rowclick', this, row, rowIndex, e);
8154 onDblClick : function(e,el)
8156 var cell = Roo.get(el);
8158 if(!cell || (!this.cellSelection && !this.rowSelection)){
8162 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8163 cell = cell.findParent('td', false, true);
8166 if(!cell || typeof(cell) == 'undefined'){
8170 var row = cell.findParent('tr', false, true);
8172 if(!row || typeof(row) == 'undefined'){
8176 var cellIndex = cell.dom.cellIndex;
8177 var rowIndex = this.getRowIndex(row);
8179 if(this.cellSelection){
8180 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8183 if(this.rowSelection){
8184 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8188 sort : function(e,el)
8190 var col = Roo.get(el);
8192 if(!col.hasClass('sortable')){
8196 var sort = col.attr('sort');
8199 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8203 this.store.sortInfo = {field : sort, direction : dir};
8206 Roo.log("calling footer first");
8207 this.footer.onClick('first');
8210 this.store.load({ params : { start : 0 } });
8214 renderHeader : function()
8222 this.totalWidth = 0;
8224 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8226 var config = cm.config[i];
8230 cls : 'x-hcol-' + i,
8232 html: cm.getColumnHeader(i)
8237 if(typeof(config.sortable) != 'undefined' && config.sortable){
8239 c.html = '<i class="glyphicon"></i>' + c.html;
8242 // could use BS4 hidden-..-down
8244 if(typeof(config.lgHeader) != 'undefined'){
8245 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8248 if(typeof(config.mdHeader) != 'undefined'){
8249 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8252 if(typeof(config.smHeader) != 'undefined'){
8253 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8256 if(typeof(config.xsHeader) != 'undefined'){
8257 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8264 if(typeof(config.tooltip) != 'undefined'){
8265 c.tooltip = config.tooltip;
8268 if(typeof(config.colspan) != 'undefined'){
8269 c.colspan = config.colspan;
8272 if(typeof(config.hidden) != 'undefined' && config.hidden){
8273 c.style += ' display:none;';
8276 if(typeof(config.dataIndex) != 'undefined'){
8277 c.sort = config.dataIndex;
8282 if(typeof(config.align) != 'undefined' && config.align.length){
8283 c.style += ' text-align:' + config.align + ';';
8286 if(typeof(config.width) != 'undefined'){
8287 c.style += ' width:' + config.width + 'px;';
8288 this.totalWidth += config.width;
8290 this.totalWidth += 100; // assume minimum of 100 per column?
8293 if(typeof(config.cls) != 'undefined'){
8294 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8297 ['xs','sm','md','lg'].map(function(size){
8299 if(typeof(config[size]) == 'undefined'){
8303 if (!config[size]) { // 0 = hidden
8304 // BS 4 '0' is treated as hide that column and below.
8305 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8309 c.cls += ' col-' + size + '-' + config[size] + (
8310 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8322 renderBody : function()
8332 colspan : this.cm.getColumnCount()
8342 renderFooter : function()
8352 colspan : this.cm.getColumnCount()
8366 // Roo.log('ds onload');
8371 var ds = this.store;
8373 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8374 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8375 if (_this.store.sortInfo) {
8377 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8378 e.select('i', true).addClass(['glyphicon-arrow-up']);
8381 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8382 e.select('i', true).addClass(['glyphicon-arrow-down']);
8387 var tbody = this.mainBody;
8389 if(ds.getCount() > 0){
8390 ds.data.each(function(d,rowIndex){
8391 var row = this.renderRow(cm, ds, rowIndex);
8393 tbody.createChild(row);
8397 if(row.cellObjects.length){
8398 Roo.each(row.cellObjects, function(r){
8399 _this.renderCellObject(r);
8406 var tfoot = this.el.select('tfoot', true).first();
8408 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8410 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8412 var total = this.ds.getTotalCount();
8414 if(this.footer.pageSize < total){
8415 this.mainFoot.show();
8419 Roo.each(this.el.select('tbody td', true).elements, function(e){
8420 e.on('mouseover', _this.onMouseover, _this);
8423 Roo.each(this.el.select('tbody td', true).elements, function(e){
8424 e.on('mouseout', _this.onMouseout, _this);
8426 this.fireEvent('rowsrendered', this);
8432 onUpdate : function(ds,record)
8434 this.refreshRow(record);
8438 onRemove : function(ds, record, index, isUpdate){
8439 if(isUpdate !== true){
8440 this.fireEvent("beforerowremoved", this, index, record);
8442 var bt = this.mainBody.dom;
8444 var rows = this.el.select('tbody > tr', true).elements;
8446 if(typeof(rows[index]) != 'undefined'){
8447 bt.removeChild(rows[index].dom);
8450 // if(bt.rows[index]){
8451 // bt.removeChild(bt.rows[index]);
8454 if(isUpdate !== true){
8455 //this.stripeRows(index);
8456 //this.syncRowHeights(index, index);
8458 this.fireEvent("rowremoved", this, index, record);
8462 onAdd : function(ds, records, rowIndex)
8464 //Roo.log('on Add called');
8465 // - note this does not handle multiple adding very well..
8466 var bt = this.mainBody.dom;
8467 for (var i =0 ; i < records.length;i++) {
8468 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8469 //Roo.log(records[i]);
8470 //Roo.log(this.store.getAt(rowIndex+i));
8471 this.insertRow(this.store, rowIndex + i, false);
8478 refreshRow : function(record){
8479 var ds = this.store, index;
8480 if(typeof record == 'number'){
8482 record = ds.getAt(index);
8484 index = ds.indexOf(record);
8486 return; // should not happen - but seems to
8489 this.insertRow(ds, index, true);
8491 this.onRemove(ds, record, index+1, true);
8493 //this.syncRowHeights(index, index);
8495 this.fireEvent("rowupdated", this, index, record);
8498 insertRow : function(dm, rowIndex, isUpdate){
8501 this.fireEvent("beforerowsinserted", this, rowIndex);
8503 //var s = this.getScrollState();
8504 var row = this.renderRow(this.cm, this.store, rowIndex);
8505 // insert before rowIndex..
8506 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8510 if(row.cellObjects.length){
8511 Roo.each(row.cellObjects, function(r){
8512 _this.renderCellObject(r);
8517 this.fireEvent("rowsinserted", this, rowIndex);
8518 //this.syncRowHeights(firstRow, lastRow);
8519 //this.stripeRows(firstRow);
8526 getRowDom : function(rowIndex)
8528 var rows = this.el.select('tbody > tr', true).elements;
8530 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8533 // returns the object tree for a tr..
8536 renderRow : function(cm, ds, rowIndex)
8538 var d = ds.getAt(rowIndex);
8542 cls : 'x-row-' + rowIndex,
8546 var cellObjects = [];
8548 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8549 var config = cm.config[i];
8551 var renderer = cm.getRenderer(i);
8555 if(typeof(renderer) !== 'undefined'){
8556 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8558 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8559 // and are rendered into the cells after the row is rendered - using the id for the element.
8561 if(typeof(value) === 'object'){
8571 rowIndex : rowIndex,
8576 this.fireEvent('rowclass', this, rowcfg);
8580 cls : rowcfg.rowClass + ' x-col-' + i,
8582 html: (typeof(value) === 'object') ? '' : value
8589 if(typeof(config.colspan) != 'undefined'){
8590 td.colspan = config.colspan;
8593 if(typeof(config.hidden) != 'undefined' && config.hidden){
8594 td.style += ' display:none;';
8597 if(typeof(config.align) != 'undefined' && config.align.length){
8598 td.style += ' text-align:' + config.align + ';';
8600 if(typeof(config.valign) != 'undefined' && config.valign.length){
8601 td.style += ' vertical-align:' + config.valign + ';';
8604 if(typeof(config.width) != 'undefined'){
8605 td.style += ' width:' + config.width + 'px;';
8608 if(typeof(config.cursor) != 'undefined'){
8609 td.style += ' cursor:' + config.cursor + ';';
8612 if(typeof(config.cls) != 'undefined'){
8613 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8616 ['xs','sm','md','lg'].map(function(size){
8618 if(typeof(config[size]) == 'undefined'){
8624 if (!config[size]) { // 0 = hidden
8625 // BS 4 '0' is treated as hide that column and below.
8626 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8630 td.cls += ' col-' + size + '-' + config[size] + (
8631 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8641 row.cellObjects = cellObjects;
8649 onBeforeLoad : function()
8658 this.el.select('tbody', true).first().dom.innerHTML = '';
8661 * Show or hide a row.
8662 * @param {Number} rowIndex to show or hide
8663 * @param {Boolean} state hide
8665 setRowVisibility : function(rowIndex, state)
8667 var bt = this.mainBody.dom;
8669 var rows = this.el.select('tbody > tr', true).elements;
8671 if(typeof(rows[rowIndex]) == 'undefined'){
8674 rows[rowIndex].dom.style.display = state ? '' : 'none';
8678 getSelectionModel : function(){
8680 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8682 return this.selModel;
8685 * Render the Roo.bootstrap object from renderder
8687 renderCellObject : function(r)
8691 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8693 var t = r.cfg.render(r.container);
8696 Roo.each(r.cfg.cn, function(c){
8698 container: t.getChildContainer(),
8701 _this.renderCellObject(child);
8706 getRowIndex : function(row)
8710 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8721 * Returns the grid's underlying element = used by panel.Grid
8722 * @return {Element} The element
8724 getGridEl : function(){
8728 * Forces a resize - used by panel.Grid
8729 * @return {Element} The element
8731 autoSize : function()
8733 //var ctr = Roo.get(this.container.dom.parentElement);
8734 var ctr = Roo.get(this.el.dom);
8736 var thd = this.getGridEl().select('thead',true).first();
8737 var tbd = this.getGridEl().select('tbody', true).first();
8738 var tfd = this.getGridEl().select('tfoot', true).first();
8740 var cw = ctr.getWidth();
8741 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8745 tbd.setWidth(ctr.getWidth());
8746 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8747 // this needs fixing for various usage - currently only hydra job advers I think..
8749 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8751 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8754 cw = Math.max(cw, this.totalWidth);
8755 this.getGridEl().select('tbody tr',true).setWidth(cw);
8757 // resize 'expandable coloumn?
8759 return; // we doe not have a view in this design..
8762 onBodyScroll: function()
8764 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8766 this.mainHead.setStyle({
8767 'position' : 'relative',
8768 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8774 var scrollHeight = this.mainBody.dom.scrollHeight;
8776 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8778 var height = this.mainBody.getHeight();
8780 if(scrollHeight - height == scrollTop) {
8782 var total = this.ds.getTotalCount();
8784 if(this.footer.cursor + this.footer.pageSize < total){
8786 this.footer.ds.load({
8788 start : this.footer.cursor + this.footer.pageSize,
8789 limit : this.footer.pageSize
8799 onHeaderChange : function()
8801 var header = this.renderHeader();
8802 var table = this.el.select('table', true).first();
8804 this.mainHead.remove();
8805 this.mainHead = table.createChild(header, this.mainBody, false);
8808 onHiddenChange : function(colModel, colIndex, hidden)
8810 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8811 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8813 this.CSS.updateRule(thSelector, "display", "");
8814 this.CSS.updateRule(tdSelector, "display", "");
8817 this.CSS.updateRule(thSelector, "display", "none");
8818 this.CSS.updateRule(tdSelector, "display", "none");
8821 this.onHeaderChange();
8825 setColumnWidth: function(col_index, width)
8827 // width = "md-2 xs-2..."
8828 if(!this.colModel.config[col_index]) {
8832 var w = width.split(" ");
8834 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8836 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8839 for(var j = 0; j < w.length; j++) {
8845 var size_cls = w[j].split("-");
8847 if(!Number.isInteger(size_cls[1] * 1)) {
8851 if(!this.colModel.config[col_index][size_cls[0]]) {
8855 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8859 h_row[0].classList.replace(
8860 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8861 "col-"+size_cls[0]+"-"+size_cls[1]
8864 for(var i = 0; i < rows.length; i++) {
8866 var size_cls = w[j].split("-");
8868 if(!Number.isInteger(size_cls[1] * 1)) {
8872 if(!this.colModel.config[col_index][size_cls[0]]) {
8876 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8880 rows[i].classList.replace(
8881 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8882 "col-"+size_cls[0]+"-"+size_cls[1]
8886 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8901 * @class Roo.bootstrap.TableCell
8902 * @extends Roo.bootstrap.Component
8903 * Bootstrap TableCell class
8904 * @cfg {String} html cell contain text
8905 * @cfg {String} cls cell class
8906 * @cfg {String} tag cell tag (td|th) default td
8907 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8908 * @cfg {String} align Aligns the content in a cell
8909 * @cfg {String} axis Categorizes cells
8910 * @cfg {String} bgcolor Specifies the background color of a cell
8911 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8912 * @cfg {Number} colspan Specifies the number of columns a cell should span
8913 * @cfg {String} headers Specifies one or more header cells a cell is related to
8914 * @cfg {Number} height Sets the height of a cell
8915 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8916 * @cfg {Number} rowspan Sets the number of rows a cell should span
8917 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8918 * @cfg {String} valign Vertical aligns the content in a cell
8919 * @cfg {Number} width Specifies the width of a cell
8922 * Create a new TableCell
8923 * @param {Object} config The config object
8926 Roo.bootstrap.TableCell = function(config){
8927 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8930 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8950 getAutoCreate : function(){
8951 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8971 cfg.align=this.align
8977 cfg.bgcolor=this.bgcolor
8980 cfg.charoff=this.charoff
8983 cfg.colspan=this.colspan
8986 cfg.headers=this.headers
8989 cfg.height=this.height
8992 cfg.nowrap=this.nowrap
8995 cfg.rowspan=this.rowspan
8998 cfg.scope=this.scope
9001 cfg.valign=this.valign
9004 cfg.width=this.width
9023 * @class Roo.bootstrap.TableRow
9024 * @extends Roo.bootstrap.Component
9025 * Bootstrap TableRow class
9026 * @cfg {String} cls row class
9027 * @cfg {String} align Aligns the content in a table row
9028 * @cfg {String} bgcolor Specifies a background color for a table row
9029 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9030 * @cfg {String} valign Vertical aligns the content in a table row
9033 * Create a new TableRow
9034 * @param {Object} config The config object
9037 Roo.bootstrap.TableRow = function(config){
9038 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9041 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9049 getAutoCreate : function(){
9050 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9060 cfg.align = this.align;
9063 cfg.bgcolor = this.bgcolor;
9066 cfg.charoff = this.charoff;
9069 cfg.valign = this.valign;
9087 * @class Roo.bootstrap.TableBody
9088 * @extends Roo.bootstrap.Component
9089 * Bootstrap TableBody class
9090 * @cfg {String} cls element class
9091 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9092 * @cfg {String} align Aligns the content inside the element
9093 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9094 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9097 * Create a new TableBody
9098 * @param {Object} config The config object
9101 Roo.bootstrap.TableBody = function(config){
9102 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9105 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9113 getAutoCreate : function(){
9114 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9128 cfg.align = this.align;
9131 cfg.charoff = this.charoff;
9134 cfg.valign = this.valign;
9141 // initEvents : function()
9148 // this.store = Roo.factory(this.store, Roo.data);
9149 // this.store.on('load', this.onLoad, this);
9151 // this.store.load();
9155 // onLoad: function ()
9157 // this.fireEvent('load', this);
9167 * Ext JS Library 1.1.1
9168 * Copyright(c) 2006-2007, Ext JS, LLC.
9170 * Originally Released Under LGPL - original licence link has changed is not relivant.
9173 * <script type="text/javascript">
9176 // as we use this in bootstrap.
9177 Roo.namespace('Roo.form');
9179 * @class Roo.form.Action
9180 * Internal Class used to handle form actions
9182 * @param {Roo.form.BasicForm} el The form element or its id
9183 * @param {Object} config Configuration options
9188 // define the action interface
9189 Roo.form.Action = function(form, options){
9191 this.options = options || {};
9194 * Client Validation Failed
9197 Roo.form.Action.CLIENT_INVALID = 'client';
9199 * Server Validation Failed
9202 Roo.form.Action.SERVER_INVALID = 'server';
9204 * Connect to Server Failed
9207 Roo.form.Action.CONNECT_FAILURE = 'connect';
9209 * Reading Data from Server Failed
9212 Roo.form.Action.LOAD_FAILURE = 'load';
9214 Roo.form.Action.prototype = {
9216 failureType : undefined,
9217 response : undefined,
9221 run : function(options){
9226 success : function(response){
9231 handleResponse : function(response){
9235 // default connection failure
9236 failure : function(response){
9238 this.response = response;
9239 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9240 this.form.afterAction(this, false);
9243 processResponse : function(response){
9244 this.response = response;
9245 if(!response.responseText){
9248 this.result = this.handleResponse(response);
9252 // utility functions used internally
9253 getUrl : function(appendParams){
9254 var url = this.options.url || this.form.url || this.form.el.dom.action;
9256 var p = this.getParams();
9258 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9264 getMethod : function(){
9265 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9268 getParams : function(){
9269 var bp = this.form.baseParams;
9270 var p = this.options.params;
9272 if(typeof p == "object"){
9273 p = Roo.urlEncode(Roo.applyIf(p, bp));
9274 }else if(typeof p == 'string' && bp){
9275 p += '&' + Roo.urlEncode(bp);
9278 p = Roo.urlEncode(bp);
9283 createCallback : function(){
9285 success: this.success,
9286 failure: this.failure,
9288 timeout: (this.form.timeout*1000),
9289 upload: this.form.fileUpload ? this.success : undefined
9294 Roo.form.Action.Submit = function(form, options){
9295 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9298 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9301 haveProgress : false,
9302 uploadComplete : false,
9304 // uploadProgress indicator.
9305 uploadProgress : function()
9307 if (!this.form.progressUrl) {
9311 if (!this.haveProgress) {
9312 Roo.MessageBox.progress("Uploading", "Uploading");
9314 if (this.uploadComplete) {
9315 Roo.MessageBox.hide();
9319 this.haveProgress = true;
9321 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9323 var c = new Roo.data.Connection();
9325 url : this.form.progressUrl,
9330 success : function(req){
9331 //console.log(data);
9335 rdata = Roo.decode(req.responseText)
9337 Roo.log("Invalid data from server..");
9341 if (!rdata || !rdata.success) {
9343 Roo.MessageBox.alert(Roo.encode(rdata));
9346 var data = rdata.data;
9348 if (this.uploadComplete) {
9349 Roo.MessageBox.hide();
9354 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9355 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9358 this.uploadProgress.defer(2000,this);
9361 failure: function(data) {
9362 Roo.log('progress url failed ');
9373 // run get Values on the form, so it syncs any secondary forms.
9374 this.form.getValues();
9376 var o = this.options;
9377 var method = this.getMethod();
9378 var isPost = method == 'POST';
9379 if(o.clientValidation === false || this.form.isValid()){
9381 if (this.form.progressUrl) {
9382 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9383 (new Date() * 1) + '' + Math.random());
9388 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9389 form:this.form.el.dom,
9390 url:this.getUrl(!isPost),
9392 params:isPost ? this.getParams() : null,
9393 isUpload: this.form.fileUpload,
9394 formData : this.form.formData
9397 this.uploadProgress();
9399 }else if (o.clientValidation !== false){ // client validation failed
9400 this.failureType = Roo.form.Action.CLIENT_INVALID;
9401 this.form.afterAction(this, false);
9405 success : function(response)
9407 this.uploadComplete= true;
9408 if (this.haveProgress) {
9409 Roo.MessageBox.hide();
9413 var result = this.processResponse(response);
9414 if(result === true || result.success){
9415 this.form.afterAction(this, true);
9419 this.form.markInvalid(result.errors);
9420 this.failureType = Roo.form.Action.SERVER_INVALID;
9422 this.form.afterAction(this, false);
9424 failure : function(response)
9426 this.uploadComplete= true;
9427 if (this.haveProgress) {
9428 Roo.MessageBox.hide();
9431 this.response = response;
9432 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9433 this.form.afterAction(this, false);
9436 handleResponse : function(response){
9437 if(this.form.errorReader){
9438 var rs = this.form.errorReader.read(response);
9441 for(var i = 0, len = rs.records.length; i < len; i++) {
9442 var r = rs.records[i];
9446 if(errors.length < 1){
9450 success : rs.success,
9456 ret = Roo.decode(response.responseText);
9460 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9470 Roo.form.Action.Load = function(form, options){
9471 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9472 this.reader = this.form.reader;
9475 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9480 Roo.Ajax.request(Roo.apply(
9481 this.createCallback(), {
9482 method:this.getMethod(),
9483 url:this.getUrl(false),
9484 params:this.getParams()
9488 success : function(response){
9490 var result = this.processResponse(response);
9491 if(result === true || !result.success || !result.data){
9492 this.failureType = Roo.form.Action.LOAD_FAILURE;
9493 this.form.afterAction(this, false);
9496 this.form.clearInvalid();
9497 this.form.setValues(result.data);
9498 this.form.afterAction(this, true);
9501 handleResponse : function(response){
9502 if(this.form.reader){
9503 var rs = this.form.reader.read(response);
9504 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9506 success : rs.success,
9510 return Roo.decode(response.responseText);
9514 Roo.form.Action.ACTION_TYPES = {
9515 'load' : Roo.form.Action.Load,
9516 'submit' : Roo.form.Action.Submit
9525 * @class Roo.bootstrap.Form
9526 * @extends Roo.bootstrap.Component
9527 * Bootstrap Form class
9528 * @cfg {String} method GET | POST (default POST)
9529 * @cfg {String} labelAlign top | left (default top)
9530 * @cfg {String} align left | right - for navbars
9531 * @cfg {Boolean} loadMask load mask when submit (default true)
9536 * @param {Object} config The config object
9540 Roo.bootstrap.Form = function(config){
9542 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9544 Roo.bootstrap.Form.popover.apply();
9548 * @event clientvalidation
9549 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9550 * @param {Form} this
9551 * @param {Boolean} valid true if the form has passed client-side validation
9553 clientvalidation: true,
9555 * @event beforeaction
9556 * Fires before any action is performed. Return false to cancel the action.
9557 * @param {Form} this
9558 * @param {Action} action The action to be performed
9562 * @event actionfailed
9563 * Fires when an action fails.
9564 * @param {Form} this
9565 * @param {Action} action The action that failed
9567 actionfailed : true,
9569 * @event actioncomplete
9570 * Fires when an action is completed.
9571 * @param {Form} this
9572 * @param {Action} action The action that completed
9574 actioncomplete : true
9578 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9581 * @cfg {String} method
9582 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9587 * The URL to use for form actions if one isn't supplied in the action options.
9590 * @cfg {Boolean} fileUpload
9591 * Set to true if this form is a file upload.
9595 * @cfg {Object} baseParams
9596 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9600 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9604 * @cfg {Sting} align (left|right) for navbar forms
9609 activeAction : null,
9612 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9613 * element by passing it or its id or mask the form itself by passing in true.
9616 waitMsgTarget : false,
9621 * @cfg {Boolean} errorMask (true|false) default false
9626 * @cfg {Number} maskOffset Default 100
9631 * @cfg {Boolean} maskBody
9635 getAutoCreate : function(){
9639 method : this.method || 'POST',
9640 id : this.id || Roo.id(),
9643 if (this.parent().xtype.match(/^Nav/)) {
9644 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9648 if (this.labelAlign == 'left' ) {
9649 cfg.cls += ' form-horizontal';
9655 initEvents : function()
9657 this.el.on('submit', this.onSubmit, this);
9658 // this was added as random key presses on the form where triggering form submit.
9659 this.el.on('keypress', function(e) {
9660 if (e.getCharCode() != 13) {
9663 // we might need to allow it for textareas.. and some other items.
9664 // check e.getTarget().
9666 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9670 Roo.log("keypress blocked");
9678 onSubmit : function(e){
9683 * Returns true if client-side validation on the form is successful.
9686 isValid : function(){
9687 var items = this.getItems();
9691 items.each(function(f){
9697 Roo.log('invalid field: ' + f.name);
9701 if(!target && f.el.isVisible(true)){
9707 if(this.errorMask && !valid){
9708 Roo.bootstrap.Form.popover.mask(this, target);
9715 * Returns true if any fields in this form have changed since their original load.
9718 isDirty : function(){
9720 var items = this.getItems();
9721 items.each(function(f){
9731 * Performs a predefined action (submit or load) or custom actions you define on this form.
9732 * @param {String} actionName The name of the action type
9733 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9734 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9735 * accept other config options):
9737 Property Type Description
9738 ---------------- --------------- ----------------------------------------------------------------------------------
9739 url String The url for the action (defaults to the form's url)
9740 method String The form method to use (defaults to the form's method, or POST if not defined)
9741 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9742 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9743 validate the form on the client (defaults to false)
9745 * @return {BasicForm} this
9747 doAction : function(action, options){
9748 if(typeof action == 'string'){
9749 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9751 if(this.fireEvent('beforeaction', this, action) !== false){
9752 this.beforeAction(action);
9753 action.run.defer(100, action);
9759 beforeAction : function(action){
9760 var o = action.options;
9765 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9767 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9770 // not really supported yet.. ??
9772 //if(this.waitMsgTarget === true){
9773 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774 //}else if(this.waitMsgTarget){
9775 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9776 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9778 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9784 afterAction : function(action, success){
9785 this.activeAction = null;
9786 var o = action.options;
9791 Roo.get(document.body).unmask();
9797 //if(this.waitMsgTarget === true){
9798 // this.el.unmask();
9799 //}else if(this.waitMsgTarget){
9800 // this.waitMsgTarget.unmask();
9802 // Roo.MessageBox.updateProgress(1);
9803 // Roo.MessageBox.hide();
9810 Roo.callback(o.success, o.scope, [this, action]);
9811 this.fireEvent('actioncomplete', this, action);
9815 // failure condition..
9816 // we have a scenario where updates need confirming.
9817 // eg. if a locking scenario exists..
9818 // we look for { errors : { needs_confirm : true }} in the response.
9820 (typeof(action.result) != 'undefined') &&
9821 (typeof(action.result.errors) != 'undefined') &&
9822 (typeof(action.result.errors.needs_confirm) != 'undefined')
9825 Roo.log("not supported yet");
9828 Roo.MessageBox.confirm(
9829 "Change requires confirmation",
9830 action.result.errorMsg,
9835 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9845 Roo.callback(o.failure, o.scope, [this, action]);
9846 // show an error message if no failed handler is set..
9847 if (!this.hasListener('actionfailed')) {
9848 Roo.log("need to add dialog support");
9850 Roo.MessageBox.alert("Error",
9851 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9852 action.result.errorMsg :
9853 "Saving Failed, please check your entries or try again"
9858 this.fireEvent('actionfailed', this, action);
9863 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9864 * @param {String} id The value to search for
9867 findField : function(id){
9868 var items = this.getItems();
9869 var field = items.get(id);
9871 items.each(function(f){
9872 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9879 return field || null;
9882 * Mark fields in this form invalid in bulk.
9883 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9884 * @return {BasicForm} this
9886 markInvalid : function(errors){
9887 if(errors instanceof Array){
9888 for(var i = 0, len = errors.length; i < len; i++){
9889 var fieldError = errors[i];
9890 var f = this.findField(fieldError.id);
9892 f.markInvalid(fieldError.msg);
9898 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9899 field.markInvalid(errors[id]);
9903 //Roo.each(this.childForms || [], function (f) {
9904 // f.markInvalid(errors);
9911 * Set values for fields in this form in bulk.
9912 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9913 * @return {BasicForm} this
9915 setValues : function(values){
9916 if(values instanceof Array){ // array of objects
9917 for(var i = 0, len = values.length; i < len; i++){
9919 var f = this.findField(v.id);
9921 f.setValue(v.value);
9922 if(this.trackResetOnLoad){
9923 f.originalValue = f.getValue();
9927 }else{ // object hash
9930 if(typeof values[id] != 'function' && (field = this.findField(id))){
9932 if (field.setFromData &&
9934 field.displayField &&
9935 // combos' with local stores can
9936 // be queried via setValue()
9937 // to set their value..
9938 (field.store && !field.store.isLocal)
9942 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9943 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9944 field.setFromData(sd);
9946 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9948 field.setFromData(values);
9951 field.setValue(values[id]);
9955 if(this.trackResetOnLoad){
9956 field.originalValue = field.getValue();
9962 //Roo.each(this.childForms || [], function (f) {
9963 // f.setValues(values);
9970 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9971 * they are returned as an array.
9972 * @param {Boolean} asString
9975 getValues : function(asString){
9976 //if (this.childForms) {
9977 // copy values from the child forms
9978 // Roo.each(this.childForms, function (f) {
9979 // this.setValues(f.getValues());
9985 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9986 if(asString === true){
9989 return Roo.urlDecode(fs);
9993 * Returns the fields in this form as an object with key/value pairs.
9994 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9997 getFieldValues : function(with_hidden)
9999 var items = this.getItems();
10001 items.each(function(f){
10003 if (!f.getName()) {
10007 var v = f.getValue();
10009 if (f.inputType =='radio') {
10010 if (typeof(ret[f.getName()]) == 'undefined') {
10011 ret[f.getName()] = ''; // empty..
10014 if (!f.el.dom.checked) {
10018 v = f.el.dom.value;
10022 if(f.xtype == 'MoneyField'){
10023 ret[f.currencyName] = f.getCurrency();
10026 // not sure if this supported any more..
10027 if ((typeof(v) == 'object') && f.getRawValue) {
10028 v = f.getRawValue() ; // dates..
10030 // combo boxes where name != hiddenName...
10031 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10032 ret[f.name] = f.getRawValue();
10034 ret[f.getName()] = v;
10041 * Clears all invalid messages in this form.
10042 * @return {BasicForm} this
10044 clearInvalid : function(){
10045 var items = this.getItems();
10047 items.each(function(f){
10055 * Resets this form.
10056 * @return {BasicForm} this
10058 reset : function(){
10059 var items = this.getItems();
10060 items.each(function(f){
10064 Roo.each(this.childForms || [], function (f) {
10072 getItems : function()
10074 var r=new Roo.util.MixedCollection(false, function(o){
10075 return o.id || (o.id = Roo.id());
10077 var iter = function(el) {
10084 Roo.each(el.items,function(e) {
10093 hideFields : function(items)
10095 Roo.each(items, function(i){
10097 var f = this.findField(i);
10108 showFields : function(items)
10110 Roo.each(items, function(i){
10112 var f = this.findField(i);
10125 Roo.apply(Roo.bootstrap.Form, {
10141 intervalID : false,
10147 if(this.isApplied){
10152 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10153 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10154 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10155 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10158 this.maskEl.top.enableDisplayMode("block");
10159 this.maskEl.left.enableDisplayMode("block");
10160 this.maskEl.bottom.enableDisplayMode("block");
10161 this.maskEl.right.enableDisplayMode("block");
10163 this.toolTip = new Roo.bootstrap.Tooltip({
10164 cls : 'roo-form-error-popover',
10166 'left' : ['r-l', [-2,0], 'right'],
10167 'right' : ['l-r', [2,0], 'left'],
10168 'bottom' : ['tl-bl', [0,2], 'top'],
10169 'top' : [ 'bl-tl', [0,-2], 'bottom']
10173 this.toolTip.render(Roo.get(document.body));
10175 this.toolTip.el.enableDisplayMode("block");
10177 Roo.get(document.body).on('click', function(){
10181 Roo.get(document.body).on('touchstart', function(){
10185 this.isApplied = true
10188 mask : function(form, target)
10192 this.target = target;
10194 if(!this.form.errorMask || !target.el){
10198 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10200 Roo.log(scrollable);
10202 var ot = this.target.el.calcOffsetsTo(scrollable);
10204 var scrollTo = ot[1] - this.form.maskOffset;
10206 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10208 scrollable.scrollTo('top', scrollTo);
10210 var box = this.target.el.getBox();
10212 var zIndex = Roo.bootstrap.Modal.zIndex++;
10215 this.maskEl.top.setStyle('position', 'absolute');
10216 this.maskEl.top.setStyle('z-index', zIndex);
10217 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10218 this.maskEl.top.setLeft(0);
10219 this.maskEl.top.setTop(0);
10220 this.maskEl.top.show();
10222 this.maskEl.left.setStyle('position', 'absolute');
10223 this.maskEl.left.setStyle('z-index', zIndex);
10224 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10225 this.maskEl.left.setLeft(0);
10226 this.maskEl.left.setTop(box.y - this.padding);
10227 this.maskEl.left.show();
10229 this.maskEl.bottom.setStyle('position', 'absolute');
10230 this.maskEl.bottom.setStyle('z-index', zIndex);
10231 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10232 this.maskEl.bottom.setLeft(0);
10233 this.maskEl.bottom.setTop(box.bottom + this.padding);
10234 this.maskEl.bottom.show();
10236 this.maskEl.right.setStyle('position', 'absolute');
10237 this.maskEl.right.setStyle('z-index', zIndex);
10238 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10239 this.maskEl.right.setLeft(box.right + this.padding);
10240 this.maskEl.right.setTop(box.y - this.padding);
10241 this.maskEl.right.show();
10243 this.toolTip.bindEl = this.target.el;
10245 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10247 var tip = this.target.blankText;
10249 if(this.target.getValue() !== '' ) {
10251 if (this.target.invalidText.length) {
10252 tip = this.target.invalidText;
10253 } else if (this.target.regexText.length){
10254 tip = this.target.regexText;
10258 this.toolTip.show(tip);
10260 this.intervalID = window.setInterval(function() {
10261 Roo.bootstrap.Form.popover.unmask();
10264 window.onwheel = function(){ return false;};
10266 (function(){ this.isMasked = true; }).defer(500, this);
10270 unmask : function()
10272 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10276 this.maskEl.top.setStyle('position', 'absolute');
10277 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10278 this.maskEl.top.hide();
10280 this.maskEl.left.setStyle('position', 'absolute');
10281 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10282 this.maskEl.left.hide();
10284 this.maskEl.bottom.setStyle('position', 'absolute');
10285 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10286 this.maskEl.bottom.hide();
10288 this.maskEl.right.setStyle('position', 'absolute');
10289 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10290 this.maskEl.right.hide();
10292 this.toolTip.hide();
10294 this.toolTip.el.hide();
10296 window.onwheel = function(){ return true;};
10298 if(this.intervalID){
10299 window.clearInterval(this.intervalID);
10300 this.intervalID = false;
10303 this.isMasked = false;
10313 * Ext JS Library 1.1.1
10314 * Copyright(c) 2006-2007, Ext JS, LLC.
10316 * Originally Released Under LGPL - original licence link has changed is not relivant.
10319 * <script type="text/javascript">
10322 * @class Roo.form.VTypes
10323 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10326 Roo.form.VTypes = function(){
10327 // closure these in so they are only created once.
10328 var alpha = /^[a-zA-Z_]+$/;
10329 var alphanum = /^[a-zA-Z0-9_]+$/;
10330 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10331 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10333 // All these messages and functions are configurable
10336 * The function used to validate email addresses
10337 * @param {String} value The email address
10339 'email' : function(v){
10340 return email.test(v);
10343 * The error text to display when the email validation function returns false
10346 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10348 * The keystroke filter mask to be applied on email input
10351 'emailMask' : /[a-z0-9_\.\-@]/i,
10354 * The function used to validate URLs
10355 * @param {String} value The URL
10357 'url' : function(v){
10358 return url.test(v);
10361 * The error text to display when the url validation function returns false
10364 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10367 * The function used to validate alpha values
10368 * @param {String} value The value
10370 'alpha' : function(v){
10371 return alpha.test(v);
10374 * The error text to display when the alpha validation function returns false
10377 'alphaText' : 'This field should only contain letters and _',
10379 * The keystroke filter mask to be applied on alpha input
10382 'alphaMask' : /[a-z_]/i,
10385 * The function used to validate alphanumeric values
10386 * @param {String} value The value
10388 'alphanum' : function(v){
10389 return alphanum.test(v);
10392 * The error text to display when the alphanumeric validation function returns false
10395 'alphanumText' : 'This field should only contain letters, numbers and _',
10397 * The keystroke filter mask to be applied on alphanumeric input
10400 'alphanumMask' : /[a-z0-9_]/i
10410 * @class Roo.bootstrap.Input
10411 * @extends Roo.bootstrap.Component
10412 * Bootstrap Input class
10413 * @cfg {Boolean} disabled is it disabled
10414 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10415 * @cfg {String} name name of the input
10416 * @cfg {string} fieldLabel - the label associated
10417 * @cfg {string} placeholder - placeholder to put in text.
10418 * @cfg {string} before - input group add on before
10419 * @cfg {string} after - input group add on after
10420 * @cfg {string} size - (lg|sm) or leave empty..
10421 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10422 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10423 * @cfg {Number} md colspan out of 12 for computer-sized screens
10424 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10425 * @cfg {string} value default value of the input
10426 * @cfg {Number} labelWidth set the width of label
10427 * @cfg {Number} labellg set the width of label (1-12)
10428 * @cfg {Number} labelmd set the width of label (1-12)
10429 * @cfg {Number} labelsm set the width of label (1-12)
10430 * @cfg {Number} labelxs set the width of label (1-12)
10431 * @cfg {String} labelAlign (top|left)
10432 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10433 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10434 * @cfg {String} indicatorpos (left|right) default left
10435 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10436 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10437 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10439 * @cfg {String} align (left|center|right) Default left
10440 * @cfg {Boolean} forceFeedback (true|false) Default false
10443 * Create a new Input
10444 * @param {Object} config The config object
10447 Roo.bootstrap.Input = function(config){
10449 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10454 * Fires when this field receives input focus.
10455 * @param {Roo.form.Field} this
10460 * Fires when this field loses input focus.
10461 * @param {Roo.form.Field} this
10465 * @event specialkey
10466 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10467 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10468 * @param {Roo.form.Field} this
10469 * @param {Roo.EventObject} e The event object
10474 * Fires just before the field blurs if the field value has changed.
10475 * @param {Roo.form.Field} this
10476 * @param {Mixed} newValue The new value
10477 * @param {Mixed} oldValue The original value
10482 * Fires after the field has been marked as invalid.
10483 * @param {Roo.form.Field} this
10484 * @param {String} msg The validation message
10489 * Fires after the field has been validated with no errors.
10490 * @param {Roo.form.Field} this
10495 * Fires after the key up
10496 * @param {Roo.form.Field} this
10497 * @param {Roo.EventObject} e The event Object
10502 * Fires after the user pastes into input
10503 * @param {Roo.form.Field} this
10504 * @param {Roo.EventObject} e The event Object
10510 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10512 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10513 automatic validation (defaults to "keyup").
10515 validationEvent : "keyup",
10517 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10519 validateOnBlur : true,
10521 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10523 validationDelay : 250,
10525 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10527 focusClass : "x-form-focus", // not needed???
10531 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10533 invalidClass : "has-warning",
10536 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10538 validClass : "has-success",
10541 * @cfg {Boolean} hasFeedback (true|false) default true
10543 hasFeedback : true,
10546 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10548 invalidFeedbackClass : "glyphicon-warning-sign",
10551 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10553 validFeedbackClass : "glyphicon-ok",
10556 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10558 selectOnFocus : false,
10561 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10565 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10570 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10572 disableKeyFilter : false,
10575 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10579 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10583 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10585 blankText : "Please complete this mandatory field",
10588 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10592 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10594 maxLength : Number.MAX_VALUE,
10596 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10598 minLengthText : "The minimum length for this field is {0}",
10600 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10602 maxLengthText : "The maximum length for this field is {0}",
10606 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10607 * If available, this function will be called only after the basic validators all return true, and will be passed the
10608 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10612 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10613 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10614 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10618 * @cfg {String} regexText -- Depricated - use Invalid Text
10623 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10629 autocomplete: false,
10633 inputType : 'text',
10636 placeholder: false,
10641 preventMark: false,
10642 isFormField : true,
10645 labelAlign : false,
10648 formatedValue : false,
10649 forceFeedback : false,
10651 indicatorpos : 'left',
10661 parentLabelAlign : function()
10664 while (parent.parent()) {
10665 parent = parent.parent();
10666 if (typeof(parent.labelAlign) !='undefined') {
10667 return parent.labelAlign;
10674 getAutoCreate : function()
10676 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10682 if(this.inputType != 'hidden'){
10683 cfg.cls = 'form-group' //input-group
10689 type : this.inputType,
10690 value : this.value,
10691 cls : 'form-control',
10692 placeholder : this.placeholder || '',
10693 autocomplete : this.autocomplete || 'new-password'
10695 if (this.inputType == 'file') {
10696 input.style = 'overflow:hidden'; // why not in CSS?
10699 if(this.capture.length){
10700 input.capture = this.capture;
10703 if(this.accept.length){
10704 input.accept = this.accept + "/*";
10708 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10711 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10712 input.maxLength = this.maxLength;
10715 if (this.disabled) {
10716 input.disabled=true;
10719 if (this.readOnly) {
10720 input.readonly=true;
10724 input.name = this.name;
10728 input.cls += ' input-' + this.size;
10732 ['xs','sm','md','lg'].map(function(size){
10733 if (settings[size]) {
10734 cfg.cls += ' col-' + size + '-' + settings[size];
10738 var inputblock = input;
10742 cls: 'glyphicon form-control-feedback'
10745 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10748 cls : 'has-feedback',
10756 if (this.before || this.after) {
10759 cls : 'input-group',
10763 if (this.before && typeof(this.before) == 'string') {
10765 inputblock.cn.push({
10767 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10771 if (this.before && typeof(this.before) == 'object') {
10772 this.before = Roo.factory(this.before);
10774 inputblock.cn.push({
10776 cls : 'roo-input-before input-group-prepend input-group-' +
10777 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10781 inputblock.cn.push(input);
10783 if (this.after && typeof(this.after) == 'string') {
10784 inputblock.cn.push({
10786 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10790 if (this.after && typeof(this.after) == 'object') {
10791 this.after = Roo.factory(this.after);
10793 inputblock.cn.push({
10795 cls : 'roo-input-after input-group-append input-group-' +
10796 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10800 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10801 inputblock.cls += ' has-feedback';
10802 inputblock.cn.push(feedback);
10807 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10808 tooltip : 'This field is required'
10810 if (this.allowBlank ) {
10811 indicator.style = this.allowBlank ? ' display:none' : '';
10813 if (align ==='left' && this.fieldLabel.length) {
10815 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10822 cls : 'control-label col-form-label',
10823 html : this.fieldLabel
10834 var labelCfg = cfg.cn[1];
10835 var contentCfg = cfg.cn[2];
10837 if(this.indicatorpos == 'right'){
10842 cls : 'control-label col-form-label',
10846 html : this.fieldLabel
10860 labelCfg = cfg.cn[0];
10861 contentCfg = cfg.cn[1];
10865 if(this.labelWidth > 12){
10866 labelCfg.style = "width: " + this.labelWidth + 'px';
10869 if(this.labelWidth < 13 && this.labelmd == 0){
10870 this.labelmd = this.labelWidth;
10873 if(this.labellg > 0){
10874 labelCfg.cls += ' col-lg-' + this.labellg;
10875 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10878 if(this.labelmd > 0){
10879 labelCfg.cls += ' col-md-' + this.labelmd;
10880 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10883 if(this.labelsm > 0){
10884 labelCfg.cls += ' col-sm-' + this.labelsm;
10885 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10888 if(this.labelxs > 0){
10889 labelCfg.cls += ' col-xs-' + this.labelxs;
10890 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10894 } else if ( this.fieldLabel.length) {
10901 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10902 tooltip : 'This field is required',
10903 style : this.allowBlank ? ' display:none' : ''
10907 //cls : 'input-group-addon',
10908 html : this.fieldLabel
10916 if(this.indicatorpos == 'right'){
10921 //cls : 'input-group-addon',
10922 html : this.fieldLabel
10927 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10928 tooltip : 'This field is required',
10929 style : this.allowBlank ? ' display:none' : ''
10949 if (this.parentType === 'Navbar' && this.parent().bar) {
10950 cfg.cls += ' navbar-form';
10953 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10954 // on BS4 we do this only if not form
10955 cfg.cls += ' navbar-form';
10963 * return the real input element.
10965 inputEl: function ()
10967 return this.el.select('input.form-control',true).first();
10970 tooltipEl : function()
10972 return this.inputEl();
10975 indicatorEl : function()
10977 if (Roo.bootstrap.version == 4) {
10978 return false; // not enabled in v4 yet.
10981 var indicator = this.el.select('i.roo-required-indicator',true).first();
10991 setDisabled : function(v)
10993 var i = this.inputEl().dom;
10995 i.removeAttribute('disabled');
10999 i.setAttribute('disabled','true');
11001 initEvents : function()
11004 this.inputEl().on("keydown" , this.fireKey, this);
11005 this.inputEl().on("focus", this.onFocus, this);
11006 this.inputEl().on("blur", this.onBlur, this);
11008 this.inputEl().relayEvent('keyup', this);
11009 this.inputEl().relayEvent('paste', this);
11011 this.indicator = this.indicatorEl();
11013 if(this.indicator){
11014 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11017 // reference to original value for reset
11018 this.originalValue = this.getValue();
11019 //Roo.form.TextField.superclass.initEvents.call(this);
11020 if(this.validationEvent == 'keyup'){
11021 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11022 this.inputEl().on('keyup', this.filterValidation, this);
11024 else if(this.validationEvent !== false){
11025 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11028 if(this.selectOnFocus){
11029 this.on("focus", this.preFocus, this);
11032 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11033 this.inputEl().on("keypress", this.filterKeys, this);
11035 this.inputEl().relayEvent('keypress', this);
11038 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11039 this.el.on("click", this.autoSize, this);
11042 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11043 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11046 if (typeof(this.before) == 'object') {
11047 this.before.render(this.el.select('.roo-input-before',true).first());
11049 if (typeof(this.after) == 'object') {
11050 this.after.render(this.el.select('.roo-input-after',true).first());
11053 this.inputEl().on('change', this.onChange, this);
11056 filterValidation : function(e){
11057 if(!e.isNavKeyPress()){
11058 this.validationTask.delay(this.validationDelay);
11062 * Validates the field value
11063 * @return {Boolean} True if the value is valid, else false
11065 validate : function(){
11066 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11067 if(this.disabled || this.validateValue(this.getRawValue())){
11072 this.markInvalid();
11078 * Validates a value according to the field's validation rules and marks the field as invalid
11079 * if the validation fails
11080 * @param {Mixed} value The value to validate
11081 * @return {Boolean} True if the value is valid, else false
11083 validateValue : function(value)
11085 if(this.getVisibilityEl().hasClass('hidden')){
11089 if(value.length < 1) { // if it's blank
11090 if(this.allowBlank){
11096 if(value.length < this.minLength){
11099 if(value.length > this.maxLength){
11103 var vt = Roo.form.VTypes;
11104 if(!vt[this.vtype](value, this)){
11108 if(typeof this.validator == "function"){
11109 var msg = this.validator(value);
11113 if (typeof(msg) == 'string') {
11114 this.invalidText = msg;
11118 if(this.regex && !this.regex.test(value)){
11126 fireKey : function(e){
11127 //Roo.log('field ' + e.getKey());
11128 if(e.isNavKeyPress()){
11129 this.fireEvent("specialkey", this, e);
11132 focus : function (selectText){
11134 this.inputEl().focus();
11135 if(selectText === true){
11136 this.inputEl().dom.select();
11142 onFocus : function(){
11143 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11144 // this.el.addClass(this.focusClass);
11146 if(!this.hasFocus){
11147 this.hasFocus = true;
11148 this.startValue = this.getValue();
11149 this.fireEvent("focus", this);
11153 beforeBlur : Roo.emptyFn,
11157 onBlur : function(){
11159 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11160 //this.el.removeClass(this.focusClass);
11162 this.hasFocus = false;
11163 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11166 var v = this.getValue();
11167 if(String(v) !== String(this.startValue)){
11168 this.fireEvent('change', this, v, this.startValue);
11170 this.fireEvent("blur", this);
11173 onChange : function(e)
11175 var v = this.getValue();
11176 if(String(v) !== String(this.startValue)){
11177 this.fireEvent('change', this, v, this.startValue);
11183 * Resets the current field value to the originally loaded value and clears any validation messages
11185 reset : function(){
11186 this.setValue(this.originalValue);
11190 * Returns the name of the field
11191 * @return {Mixed} name The name field
11193 getName: function(){
11197 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11198 * @return {Mixed} value The field value
11200 getValue : function(){
11202 var v = this.inputEl().getValue();
11207 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11208 * @return {Mixed} value The field value
11210 getRawValue : function(){
11211 var v = this.inputEl().getValue();
11217 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11218 * @param {Mixed} value The value to set
11220 setRawValue : function(v){
11221 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11224 selectText : function(start, end){
11225 var v = this.getRawValue();
11227 start = start === undefined ? 0 : start;
11228 end = end === undefined ? v.length : end;
11229 var d = this.inputEl().dom;
11230 if(d.setSelectionRange){
11231 d.setSelectionRange(start, end);
11232 }else if(d.createTextRange){
11233 var range = d.createTextRange();
11234 range.moveStart("character", start);
11235 range.moveEnd("character", v.length-end);
11242 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11243 * @param {Mixed} value The value to set
11245 setValue : function(v){
11248 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11254 processValue : function(value){
11255 if(this.stripCharsRe){
11256 var newValue = value.replace(this.stripCharsRe, '');
11257 if(newValue !== value){
11258 this.setRawValue(newValue);
11265 preFocus : function(){
11267 if(this.selectOnFocus){
11268 this.inputEl().dom.select();
11271 filterKeys : function(e){
11272 var k = e.getKey();
11273 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11276 var c = e.getCharCode(), cc = String.fromCharCode(c);
11277 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11280 if(!this.maskRe.test(cc)){
11285 * Clear any invalid styles/messages for this field
11287 clearInvalid : function(){
11289 if(!this.el || this.preventMark){ // not rendered
11294 this.el.removeClass([this.invalidClass, 'is-invalid']);
11296 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11298 var feedback = this.el.select('.form-control-feedback', true).first();
11301 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11306 if(this.indicator){
11307 this.indicator.removeClass('visible');
11308 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11311 this.fireEvent('valid', this);
11315 * Mark this field as valid
11317 markValid : function()
11319 if(!this.el || this.preventMark){ // not rendered...
11323 this.el.removeClass([this.invalidClass, this.validClass]);
11324 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11326 var feedback = this.el.select('.form-control-feedback', true).first();
11329 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11332 if(this.indicator){
11333 this.indicator.removeClass('visible');
11334 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11342 if(this.allowBlank && !this.getRawValue().length){
11345 if (Roo.bootstrap.version == 3) {
11346 this.el.addClass(this.validClass);
11348 this.inputEl().addClass('is-valid');
11351 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11353 var feedback = this.el.select('.form-control-feedback', true).first();
11356 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11357 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11362 this.fireEvent('valid', this);
11366 * Mark this field as invalid
11367 * @param {String} msg The validation message
11369 markInvalid : function(msg)
11371 if(!this.el || this.preventMark){ // not rendered
11375 this.el.removeClass([this.invalidClass, this.validClass]);
11376 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11378 var feedback = this.el.select('.form-control-feedback', true).first();
11381 this.el.select('.form-control-feedback', true).first().removeClass(
11382 [this.invalidFeedbackClass, this.validFeedbackClass]);
11389 if(this.allowBlank && !this.getRawValue().length){
11393 if(this.indicator){
11394 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11395 this.indicator.addClass('visible');
11397 if (Roo.bootstrap.version == 3) {
11398 this.el.addClass(this.invalidClass);
11400 this.inputEl().addClass('is-invalid');
11405 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11407 var feedback = this.el.select('.form-control-feedback', true).first();
11410 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11412 if(this.getValue().length || this.forceFeedback){
11413 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11420 this.fireEvent('invalid', this, msg);
11423 SafariOnKeyDown : function(event)
11425 // this is a workaround for a password hang bug on chrome/ webkit.
11426 if (this.inputEl().dom.type != 'password') {
11430 var isSelectAll = false;
11432 if(this.inputEl().dom.selectionEnd > 0){
11433 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11435 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11436 event.preventDefault();
11441 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11443 event.preventDefault();
11444 // this is very hacky as keydown always get's upper case.
11446 var cc = String.fromCharCode(event.getCharCode());
11447 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11451 adjustWidth : function(tag, w){
11452 tag = tag.toLowerCase();
11453 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11454 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11455 if(tag == 'input'){
11458 if(tag == 'textarea'){
11461 }else if(Roo.isOpera){
11462 if(tag == 'input'){
11465 if(tag == 'textarea'){
11473 setFieldLabel : function(v)
11475 if(!this.rendered){
11479 if(this.indicatorEl()){
11480 var ar = this.el.select('label > span',true);
11482 if (ar.elements.length) {
11483 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11484 this.fieldLabel = v;
11488 var br = this.el.select('label',true);
11490 if(br.elements.length) {
11491 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11492 this.fieldLabel = v;
11496 Roo.log('Cannot Found any of label > span || label in input');
11500 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11501 this.fieldLabel = v;
11516 * @class Roo.bootstrap.TextArea
11517 * @extends Roo.bootstrap.Input
11518 * Bootstrap TextArea class
11519 * @cfg {Number} cols Specifies the visible width of a text area
11520 * @cfg {Number} rows Specifies the visible number of lines in a text area
11521 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11522 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11523 * @cfg {string} html text
11526 * Create a new TextArea
11527 * @param {Object} config The config object
11530 Roo.bootstrap.TextArea = function(config){
11531 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11535 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11545 getAutoCreate : function(){
11547 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11553 if(this.inputType != 'hidden'){
11554 cfg.cls = 'form-group' //input-group
11562 value : this.value || '',
11563 html: this.html || '',
11564 cls : 'form-control',
11565 placeholder : this.placeholder || ''
11569 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11570 input.maxLength = this.maxLength;
11574 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11578 input.cols = this.cols;
11581 if (this.readOnly) {
11582 input.readonly = true;
11586 input.name = this.name;
11590 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11594 ['xs','sm','md','lg'].map(function(size){
11595 if (settings[size]) {
11596 cfg.cls += ' col-' + size + '-' + settings[size];
11600 var inputblock = input;
11602 if(this.hasFeedback && !this.allowBlank){
11606 cls: 'glyphicon form-control-feedback'
11610 cls : 'has-feedback',
11619 if (this.before || this.after) {
11622 cls : 'input-group',
11626 inputblock.cn.push({
11628 cls : 'input-group-addon',
11633 inputblock.cn.push(input);
11635 if(this.hasFeedback && !this.allowBlank){
11636 inputblock.cls += ' has-feedback';
11637 inputblock.cn.push(feedback);
11641 inputblock.cn.push({
11643 cls : 'input-group-addon',
11650 if (align ==='left' && this.fieldLabel.length) {
11655 cls : 'control-label',
11656 html : this.fieldLabel
11667 if(this.labelWidth > 12){
11668 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11671 if(this.labelWidth < 13 && this.labelmd == 0){
11672 this.labelmd = this.labelWidth;
11675 if(this.labellg > 0){
11676 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11677 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11680 if(this.labelmd > 0){
11681 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11682 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11685 if(this.labelsm > 0){
11686 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11687 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11690 if(this.labelxs > 0){
11691 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11692 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11695 } else if ( this.fieldLabel.length) {
11700 //cls : 'input-group-addon',
11701 html : this.fieldLabel
11719 if (this.disabled) {
11720 input.disabled=true;
11727 * return the real textarea element.
11729 inputEl: function ()
11731 return this.el.select('textarea.form-control',true).first();
11735 * Clear any invalid styles/messages for this field
11737 clearInvalid : function()
11740 if(!this.el || this.preventMark){ // not rendered
11744 var label = this.el.select('label', true).first();
11745 var icon = this.el.select('i.fa-star', true).first();
11750 this.el.removeClass( this.validClass);
11751 this.inputEl().removeClass('is-invalid');
11753 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11755 var feedback = this.el.select('.form-control-feedback', true).first();
11758 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11763 this.fireEvent('valid', this);
11767 * Mark this field as valid
11769 markValid : function()
11771 if(!this.el || this.preventMark){ // not rendered
11775 this.el.removeClass([this.invalidClass, this.validClass]);
11776 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11778 var feedback = this.el.select('.form-control-feedback', true).first();
11781 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11784 if(this.disabled || this.allowBlank){
11788 var label = this.el.select('label', true).first();
11789 var icon = this.el.select('i.fa-star', true).first();
11794 if (Roo.bootstrap.version == 3) {
11795 this.el.addClass(this.validClass);
11797 this.inputEl().addClass('is-valid');
11801 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11803 var feedback = this.el.select('.form-control-feedback', true).first();
11806 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11807 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11812 this.fireEvent('valid', this);
11816 * Mark this field as invalid
11817 * @param {String} msg The validation message
11819 markInvalid : function(msg)
11821 if(!this.el || this.preventMark){ // not rendered
11825 this.el.removeClass([this.invalidClass, this.validClass]);
11826 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11828 var feedback = this.el.select('.form-control-feedback', true).first();
11831 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11834 if(this.disabled || this.allowBlank){
11838 var label = this.el.select('label', true).first();
11839 var icon = this.el.select('i.fa-star', true).first();
11841 if(!this.getValue().length && label && !icon){
11842 this.el.createChild({
11844 cls : 'text-danger fa fa-lg fa-star',
11845 tooltip : 'This field is required',
11846 style : 'margin-right:5px;'
11850 if (Roo.bootstrap.version == 3) {
11851 this.el.addClass(this.invalidClass);
11853 this.inputEl().addClass('is-invalid');
11856 // fixme ... this may be depricated need to test..
11857 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11859 var feedback = this.el.select('.form-control-feedback', true).first();
11862 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11864 if(this.getValue().length || this.forceFeedback){
11865 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11872 this.fireEvent('invalid', this, msg);
11880 * trigger field - base class for combo..
11885 * @class Roo.bootstrap.TriggerField
11886 * @extends Roo.bootstrap.Input
11887 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11888 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11889 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11890 * for which you can provide a custom implementation. For example:
11892 var trigger = new Roo.bootstrap.TriggerField();
11893 trigger.onTriggerClick = myTriggerFn;
11894 trigger.applyTo('my-field');
11897 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11898 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11899 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11900 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11901 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11904 * Create a new TriggerField.
11905 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11906 * to the base TextField)
11908 Roo.bootstrap.TriggerField = function(config){
11909 this.mimicing = false;
11910 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11913 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11915 * @cfg {String} triggerClass A CSS class to apply to the trigger
11918 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11923 * @cfg {Boolean} removable (true|false) special filter default false
11927 /** @cfg {Boolean} grow @hide */
11928 /** @cfg {Number} growMin @hide */
11929 /** @cfg {Number} growMax @hide */
11935 autoSize: Roo.emptyFn,
11939 deferHeight : true,
11942 actionMode : 'wrap',
11947 getAutoCreate : function(){
11949 var align = this.labelAlign || this.parentLabelAlign();
11954 cls: 'form-group' //input-group
11961 type : this.inputType,
11962 cls : 'form-control',
11963 autocomplete: 'new-password',
11964 placeholder : this.placeholder || ''
11968 input.name = this.name;
11971 input.cls += ' input-' + this.size;
11974 if (this.disabled) {
11975 input.disabled=true;
11978 var inputblock = input;
11980 if(this.hasFeedback && !this.allowBlank){
11984 cls: 'glyphicon form-control-feedback'
11987 if(this.removable && !this.editable ){
11989 cls : 'has-feedback',
11995 cls : 'roo-combo-removable-btn close'
12002 cls : 'has-feedback',
12011 if(this.removable && !this.editable ){
12013 cls : 'roo-removable',
12019 cls : 'roo-combo-removable-btn close'
12026 if (this.before || this.after) {
12029 cls : 'input-group',
12033 inputblock.cn.push({
12035 cls : 'input-group-addon input-group-prepend input-group-text',
12040 inputblock.cn.push(input);
12042 if(this.hasFeedback && !this.allowBlank){
12043 inputblock.cls += ' has-feedback';
12044 inputblock.cn.push(feedback);
12048 inputblock.cn.push({
12050 cls : 'input-group-addon input-group-append input-group-text',
12059 var ibwrap = inputblock;
12064 cls: 'roo-select2-choices',
12068 cls: 'roo-select2-search-field',
12080 cls: 'roo-select2-container input-group',
12085 cls: 'form-hidden-field'
12091 if(!this.multiple && this.showToggleBtn){
12097 if (this.caret != false) {
12100 cls: 'fa fa-' + this.caret
12107 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12109 Roo.bootstrap.version == 3 ? caret : '',
12112 cls: 'combobox-clear',
12126 combobox.cls += ' roo-select2-container-multi';
12130 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12131 tooltip : 'This field is required'
12133 if (Roo.bootstrap.version == 4) {
12136 style : 'display:none'
12141 if (align ==='left' && this.fieldLabel.length) {
12143 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12150 cls : 'control-label',
12151 html : this.fieldLabel
12163 var labelCfg = cfg.cn[1];
12164 var contentCfg = cfg.cn[2];
12166 if(this.indicatorpos == 'right'){
12171 cls : 'control-label',
12175 html : this.fieldLabel
12189 labelCfg = cfg.cn[0];
12190 contentCfg = cfg.cn[1];
12193 if(this.labelWidth > 12){
12194 labelCfg.style = "width: " + this.labelWidth + 'px';
12197 if(this.labelWidth < 13 && this.labelmd == 0){
12198 this.labelmd = this.labelWidth;
12201 if(this.labellg > 0){
12202 labelCfg.cls += ' col-lg-' + this.labellg;
12203 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12206 if(this.labelmd > 0){
12207 labelCfg.cls += ' col-md-' + this.labelmd;
12208 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12211 if(this.labelsm > 0){
12212 labelCfg.cls += ' col-sm-' + this.labelsm;
12213 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12216 if(this.labelxs > 0){
12217 labelCfg.cls += ' col-xs-' + this.labelxs;
12218 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12221 } else if ( this.fieldLabel.length) {
12222 // Roo.log(" label");
12227 //cls : 'input-group-addon',
12228 html : this.fieldLabel
12236 if(this.indicatorpos == 'right'){
12244 html : this.fieldLabel
12258 // Roo.log(" no label && no align");
12265 ['xs','sm','md','lg'].map(function(size){
12266 if (settings[size]) {
12267 cfg.cls += ' col-' + size + '-' + settings[size];
12278 onResize : function(w, h){
12279 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12280 // if(typeof w == 'number'){
12281 // var x = w - this.trigger.getWidth();
12282 // this.inputEl().setWidth(this.adjustWidth('input', x));
12283 // this.trigger.setStyle('left', x+'px');
12288 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12291 getResizeEl : function(){
12292 return this.inputEl();
12296 getPositionEl : function(){
12297 return this.inputEl();
12301 alignErrorIcon : function(){
12302 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12306 initEvents : function(){
12310 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12311 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12312 if(!this.multiple && this.showToggleBtn){
12313 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12314 if(this.hideTrigger){
12315 this.trigger.setDisplayed(false);
12317 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12321 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12324 if(this.removable && !this.editable && !this.tickable){
12325 var close = this.closeTriggerEl();
12328 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12329 close.on('click', this.removeBtnClick, this, close);
12333 //this.trigger.addClassOnOver('x-form-trigger-over');
12334 //this.trigger.addClassOnClick('x-form-trigger-click');
12337 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12341 closeTriggerEl : function()
12343 var close = this.el.select('.roo-combo-removable-btn', true).first();
12344 return close ? close : false;
12347 removeBtnClick : function(e, h, el)
12349 e.preventDefault();
12351 if(this.fireEvent("remove", this) !== false){
12353 this.fireEvent("afterremove", this)
12357 createList : function()
12359 this.list = Roo.get(document.body).createChild({
12360 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12361 cls: 'typeahead typeahead-long dropdown-menu shadow',
12362 style: 'display:none'
12365 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12370 initTrigger : function(){
12375 onDestroy : function(){
12377 this.trigger.removeAllListeners();
12378 // this.trigger.remove();
12381 // this.wrap.remove();
12383 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12387 onFocus : function(){
12388 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12390 if(!this.mimicing){
12391 this.wrap.addClass('x-trigger-wrap-focus');
12392 this.mimicing = true;
12393 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12394 if(this.monitorTab){
12395 this.el.on("keydown", this.checkTab, this);
12402 checkTab : function(e){
12403 if(e.getKey() == e.TAB){
12404 this.triggerBlur();
12409 onBlur : function(){
12414 mimicBlur : function(e, t){
12416 if(!this.wrap.contains(t) && this.validateBlur()){
12417 this.triggerBlur();
12423 triggerBlur : function(){
12424 this.mimicing = false;
12425 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12426 if(this.monitorTab){
12427 this.el.un("keydown", this.checkTab, this);
12429 //this.wrap.removeClass('x-trigger-wrap-focus');
12430 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12434 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12435 validateBlur : function(e, t){
12440 onDisable : function(){
12441 this.inputEl().dom.disabled = true;
12442 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12444 // this.wrap.addClass('x-item-disabled');
12449 onEnable : function(){
12450 this.inputEl().dom.disabled = false;
12451 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12453 // this.el.removeClass('x-item-disabled');
12458 onShow : function(){
12459 var ae = this.getActionEl();
12462 ae.dom.style.display = '';
12463 ae.dom.style.visibility = 'visible';
12469 onHide : function(){
12470 var ae = this.getActionEl();
12471 ae.dom.style.display = 'none';
12475 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12476 * by an implementing function.
12478 * @param {EventObject} e
12480 onTriggerClick : Roo.emptyFn
12488 * @class Roo.bootstrap.CardUploader
12489 * @extends Roo.bootstrap.Button
12490 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12491 * @cfg {Number} errorTimeout default 3000
12492 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12493 * @cfg {Array} html The button text.
12497 * Create a new CardUploader
12498 * @param {Object} config The config object
12501 Roo.bootstrap.CardUploader = function(config){
12505 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12508 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12516 * When a image is clicked on - and needs to display a slideshow or similar..
12517 * @param {Roo.bootstrap.Card} this
12518 * @param {Object} The image information data
12524 * When a the download link is clicked
12525 * @param {Roo.bootstrap.Card} this
12526 * @param {Object} The image information data contains
12533 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12536 errorTimeout : 3000,
12540 fileCollection : false,
12543 getAutoCreate : function()
12547 cls :'form-group' ,
12552 //cls : 'input-group-addon',
12553 html : this.fieldLabel
12561 value : this.value,
12562 cls : 'd-none form-control'
12567 multiple : 'multiple',
12569 cls : 'd-none roo-card-upload-selector'
12573 cls : 'roo-card-uploader-button-container w-100 mb-2'
12576 cls : 'card-columns roo-card-uploader-container'
12586 getChildContainer : function() /// what children are added to.
12588 return this.containerEl;
12591 getButtonContainer : function() /// what children are added to.
12593 return this.el.select(".roo-card-uploader-button-container").first();
12596 initEvents : function()
12599 Roo.bootstrap.Input.prototype.initEvents.call(this);
12603 xns: Roo.bootstrap,
12606 container_method : 'getButtonContainer' ,
12607 html : this.html, // fix changable?
12610 'click' : function(btn, e) {
12619 this.urlAPI = (window.createObjectURL && window) ||
12620 (window.URL && URL.revokeObjectURL && URL) ||
12621 (window.webkitURL && webkitURL);
12626 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12628 this.selectorEl.on('change', this.onFileSelected, this);
12631 this.images.forEach(function(img) {
12634 this.images = false;
12636 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12642 onClick : function(e)
12644 e.preventDefault();
12646 this.selectorEl.dom.click();
12650 onFileSelected : function(e)
12652 e.preventDefault();
12654 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12658 Roo.each(this.selectorEl.dom.files, function(file){
12659 this.addFile(file);
12668 addFile : function(file)
12671 if(typeof(file) === 'string'){
12672 throw "Add file by name?"; // should not happen
12676 if(!file || !this.urlAPI){
12686 var url = _this.urlAPI.createObjectURL( file);
12689 id : Roo.bootstrap.CardUploader.ID--,
12690 is_uploaded : false,
12694 mimetype : file.type,
12702 * addCard - add an Attachment to the uploader
12703 * @param data - the data about the image to upload
12707 title : "Title of file",
12708 is_uploaded : false,
12709 src : "http://.....",
12710 srcfile : { the File upload object },
12711 mimetype : file.type,
12714 .. any other data...
12720 addCard : function (data)
12722 // hidden input element?
12723 // if the file is not an image...
12724 //then we need to use something other that and header_image
12729 xns : Roo.bootstrap,
12730 xtype : 'CardFooter',
12733 xns : Roo.bootstrap,
12739 xns : Roo.bootstrap,
12741 html : String.format("<small>{0}</small>", data.title),
12742 cls : 'col-10 text-left',
12747 click : function() {
12749 t.fireEvent( "download", t, data );
12755 xns : Roo.bootstrap,
12757 style: 'max-height: 28px; ',
12763 click : function() {
12764 t.removeCard(data.id)
12776 var cn = this.addxtype(
12779 xns : Roo.bootstrap,
12782 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12783 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12784 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12789 initEvents : function() {
12790 Roo.bootstrap.Card.prototype.initEvents.call(this);
12792 this.imgEl = this.el.select('.card-img-top').first();
12794 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12795 this.imgEl.set({ 'pointer' : 'cursor' });
12798 this.getCardFooter().addClass('p-1');
12805 // dont' really need ot update items.
12806 // this.items.push(cn);
12807 this.fileCollection.add(cn);
12809 if (!data.srcfile) {
12810 this.updateInput();
12815 var reader = new FileReader();
12816 reader.addEventListener("load", function() {
12817 data.srcdata = reader.result;
12820 reader.readAsDataURL(data.srcfile);
12825 removeCard : function(id)
12828 var card = this.fileCollection.get(id);
12829 card.data.is_deleted = 1;
12830 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12831 //this.fileCollection.remove(card);
12832 //this.items = this.items.filter(function(e) { return e != card });
12833 // dont' really need ot update items.
12834 card.el.dom.parentNode.removeChild(card.el.dom);
12835 this.updateInput();
12841 this.fileCollection.each(function(card) {
12842 if (card.el.dom && card.el.dom.parentNode) {
12843 card.el.dom.parentNode.removeChild(card.el.dom);
12846 this.fileCollection.clear();
12847 this.updateInput();
12850 updateInput : function()
12853 this.fileCollection.each(function(e) {
12857 this.inputEl().dom.value = JSON.stringify(data);
12867 Roo.bootstrap.CardUploader.ID = -1;/*
12869 * Ext JS Library 1.1.1
12870 * Copyright(c) 2006-2007, Ext JS, LLC.
12872 * Originally Released Under LGPL - original licence link has changed is not relivant.
12875 * <script type="text/javascript">
12880 * @class Roo.data.SortTypes
12882 * Defines the default sorting (casting?) comparison functions used when sorting data.
12884 Roo.data.SortTypes = {
12886 * Default sort that does nothing
12887 * @param {Mixed} s The value being converted
12888 * @return {Mixed} The comparison value
12890 none : function(s){
12895 * The regular expression used to strip tags
12899 stripTagsRE : /<\/?[^>]+>/gi,
12902 * Strips all HTML tags to sort on text only
12903 * @param {Mixed} s The value being converted
12904 * @return {String} The comparison value
12906 asText : function(s){
12907 return String(s).replace(this.stripTagsRE, "");
12911 * Strips all HTML tags to sort on text only - Case insensitive
12912 * @param {Mixed} s The value being converted
12913 * @return {String} The comparison value
12915 asUCText : function(s){
12916 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12920 * Case insensitive string
12921 * @param {Mixed} s The value being converted
12922 * @return {String} The comparison value
12924 asUCString : function(s) {
12925 return String(s).toUpperCase();
12930 * @param {Mixed} s The value being converted
12931 * @return {Number} The comparison value
12933 asDate : function(s) {
12937 if(s instanceof Date){
12938 return s.getTime();
12940 return Date.parse(String(s));
12945 * @param {Mixed} s The value being converted
12946 * @return {Float} The comparison value
12948 asFloat : function(s) {
12949 var val = parseFloat(String(s).replace(/,/g, ""));
12958 * @param {Mixed} s The value being converted
12959 * @return {Number} The comparison value
12961 asInt : function(s) {
12962 var val = parseInt(String(s).replace(/,/g, ""));
12970 * Ext JS Library 1.1.1
12971 * Copyright(c) 2006-2007, Ext JS, LLC.
12973 * Originally Released Under LGPL - original licence link has changed is not relivant.
12976 * <script type="text/javascript">
12980 * @class Roo.data.Record
12981 * Instances of this class encapsulate both record <em>definition</em> information, and record
12982 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12983 * to access Records cached in an {@link Roo.data.Store} object.<br>
12985 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12986 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12989 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12991 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12992 * {@link #create}. The parameters are the same.
12993 * @param {Array} data An associative Array of data values keyed by the field name.
12994 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12995 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12996 * not specified an integer id is generated.
12998 Roo.data.Record = function(data, id){
12999 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13004 * Generate a constructor for a specific record layout.
13005 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13006 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13007 * Each field definition object may contain the following properties: <ul>
13008 * <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,
13009 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13010 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13011 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13012 * is being used, then this is a string containing the javascript expression to reference the data relative to
13013 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13014 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13015 * this may be omitted.</p></li>
13016 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13017 * <ul><li>auto (Default, implies no conversion)</li>
13022 * <li>date</li></ul></p></li>
13023 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13024 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13025 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13026 * by the Reader into an object that will be stored in the Record. It is passed the
13027 * following parameters:<ul>
13028 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13030 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13032 * <br>usage:<br><pre><code>
13033 var TopicRecord = Roo.data.Record.create(
13034 {name: 'title', mapping: 'topic_title'},
13035 {name: 'author', mapping: 'username'},
13036 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13037 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13038 {name: 'lastPoster', mapping: 'user2'},
13039 {name: 'excerpt', mapping: 'post_text'}
13042 var myNewRecord = new TopicRecord({
13043 title: 'Do my job please',
13046 lastPost: new Date(),
13047 lastPoster: 'Animal',
13048 excerpt: 'No way dude!'
13050 myStore.add(myNewRecord);
13055 Roo.data.Record.create = function(o){
13056 var f = function(){
13057 f.superclass.constructor.apply(this, arguments);
13059 Roo.extend(f, Roo.data.Record);
13060 var p = f.prototype;
13061 p.fields = new Roo.util.MixedCollection(false, function(field){
13064 for(var i = 0, len = o.length; i < len; i++){
13065 p.fields.add(new Roo.data.Field(o[i]));
13067 f.getField = function(name){
13068 return p.fields.get(name);
13073 Roo.data.Record.AUTO_ID = 1000;
13074 Roo.data.Record.EDIT = 'edit';
13075 Roo.data.Record.REJECT = 'reject';
13076 Roo.data.Record.COMMIT = 'commit';
13078 Roo.data.Record.prototype = {
13080 * Readonly flag - true if this record has been modified.
13089 join : function(store){
13090 this.store = store;
13094 * Set the named field to the specified value.
13095 * @param {String} name The name of the field to set.
13096 * @param {Object} value The value to set the field to.
13098 set : function(name, value){
13099 if(this.data[name] == value){
13103 if(!this.modified){
13104 this.modified = {};
13106 if(typeof this.modified[name] == 'undefined'){
13107 this.modified[name] = this.data[name];
13109 this.data[name] = value;
13110 if(!this.editing && this.store){
13111 this.store.afterEdit(this);
13116 * Get the value of the named field.
13117 * @param {String} name The name of the field to get the value of.
13118 * @return {Object} The value of the field.
13120 get : function(name){
13121 return this.data[name];
13125 beginEdit : function(){
13126 this.editing = true;
13127 this.modified = {};
13131 cancelEdit : function(){
13132 this.editing = false;
13133 delete this.modified;
13137 endEdit : function(){
13138 this.editing = false;
13139 if(this.dirty && this.store){
13140 this.store.afterEdit(this);
13145 * Usually called by the {@link Roo.data.Store} which owns the Record.
13146 * Rejects all changes made to the Record since either creation, or the last commit operation.
13147 * Modified fields are reverted to their original values.
13149 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13150 * of reject operations.
13152 reject : function(){
13153 var m = this.modified;
13155 if(typeof m[n] != "function"){
13156 this.data[n] = m[n];
13159 this.dirty = false;
13160 delete this.modified;
13161 this.editing = false;
13163 this.store.afterReject(this);
13168 * Usually called by the {@link Roo.data.Store} which owns the Record.
13169 * Commits all changes made to the Record since either creation, or the last commit operation.
13171 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13172 * of commit operations.
13174 commit : function(){
13175 this.dirty = false;
13176 delete this.modified;
13177 this.editing = false;
13179 this.store.afterCommit(this);
13184 hasError : function(){
13185 return this.error != null;
13189 clearError : function(){
13194 * Creates a copy of this record.
13195 * @param {String} id (optional) A new record id if you don't want to use this record's id
13198 copy : function(newId) {
13199 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13203 * Ext JS Library 1.1.1
13204 * Copyright(c) 2006-2007, Ext JS, LLC.
13206 * Originally Released Under LGPL - original licence link has changed is not relivant.
13209 * <script type="text/javascript">
13215 * @class Roo.data.Store
13216 * @extends Roo.util.Observable
13217 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13218 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13220 * 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
13221 * has no knowledge of the format of the data returned by the Proxy.<br>
13223 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13224 * instances from the data object. These records are cached and made available through accessor functions.
13226 * Creates a new Store.
13227 * @param {Object} config A config object containing the objects needed for the Store to access data,
13228 * and read the data into Records.
13230 Roo.data.Store = function(config){
13231 this.data = new Roo.util.MixedCollection(false);
13232 this.data.getKey = function(o){
13235 this.baseParams = {};
13237 this.paramNames = {
13242 "multisort" : "_multisort"
13245 if(config && config.data){
13246 this.inlineData = config.data;
13247 delete config.data;
13250 Roo.apply(this, config);
13252 if(this.reader){ // reader passed
13253 this.reader = Roo.factory(this.reader, Roo.data);
13254 this.reader.xmodule = this.xmodule || false;
13255 if(!this.recordType){
13256 this.recordType = this.reader.recordType;
13258 if(this.reader.onMetaChange){
13259 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13263 if(this.recordType){
13264 this.fields = this.recordType.prototype.fields;
13266 this.modified = [];
13270 * @event datachanged
13271 * Fires when the data cache has changed, and a widget which is using this Store
13272 * as a Record cache should refresh its view.
13273 * @param {Store} this
13275 datachanged : true,
13277 * @event metachange
13278 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13279 * @param {Store} this
13280 * @param {Object} meta The JSON metadata
13285 * Fires when Records have been added to the Store
13286 * @param {Store} this
13287 * @param {Roo.data.Record[]} records The array of Records added
13288 * @param {Number} index The index at which the record(s) were added
13293 * Fires when a Record has been removed from the Store
13294 * @param {Store} this
13295 * @param {Roo.data.Record} record The Record that was removed
13296 * @param {Number} index The index at which the record was removed
13301 * Fires when a Record has been updated
13302 * @param {Store} this
13303 * @param {Roo.data.Record} record The Record that was updated
13304 * @param {String} operation The update operation being performed. Value may be one of:
13306 Roo.data.Record.EDIT
13307 Roo.data.Record.REJECT
13308 Roo.data.Record.COMMIT
13314 * Fires when the data cache has been cleared.
13315 * @param {Store} this
13319 * @event beforeload
13320 * Fires before a request is made for a new data object. If the beforeload handler returns false
13321 * the load action will be canceled.
13322 * @param {Store} this
13323 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13327 * @event beforeloadadd
13328 * Fires after a new set of Records has been loaded.
13329 * @param {Store} this
13330 * @param {Roo.data.Record[]} records The Records that were loaded
13331 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13333 beforeloadadd : true,
13336 * Fires after a new set of Records has been loaded, before they are added to the store.
13337 * @param {Store} this
13338 * @param {Roo.data.Record[]} records The Records that were loaded
13339 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13340 * @params {Object} return from reader
13344 * @event loadexception
13345 * Fires if an exception occurs in the Proxy during loading.
13346 * Called with the signature of the Proxy's "loadexception" event.
13347 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13350 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13351 * @param {Object} load options
13352 * @param {Object} jsonData from your request (normally this contains the Exception)
13354 loadexception : true
13358 this.proxy = Roo.factory(this.proxy, Roo.data);
13359 this.proxy.xmodule = this.xmodule || false;
13360 this.relayEvents(this.proxy, ["loadexception"]);
13362 this.sortToggle = {};
13363 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13365 Roo.data.Store.superclass.constructor.call(this);
13367 if(this.inlineData){
13368 this.loadData(this.inlineData);
13369 delete this.inlineData;
13373 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13375 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13376 * without a remote query - used by combo/forms at present.
13380 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13383 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13386 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13387 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13390 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13391 * on any HTTP request
13394 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13397 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13401 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13402 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13404 remoteSort : false,
13407 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13408 * loaded or when a record is removed. (defaults to false).
13410 pruneModifiedRecords : false,
13413 lastOptions : null,
13416 * Add Records to the Store and fires the add event.
13417 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13419 add : function(records){
13420 records = [].concat(records);
13421 for(var i = 0, len = records.length; i < len; i++){
13422 records[i].join(this);
13424 var index = this.data.length;
13425 this.data.addAll(records);
13426 this.fireEvent("add", this, records, index);
13430 * Remove a Record from the Store and fires the remove event.
13431 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13433 remove : function(record){
13434 var index = this.data.indexOf(record);
13435 this.data.removeAt(index);
13437 if(this.pruneModifiedRecords){
13438 this.modified.remove(record);
13440 this.fireEvent("remove", this, record, index);
13444 * Remove all Records from the Store and fires the clear event.
13446 removeAll : function(){
13448 if(this.pruneModifiedRecords){
13449 this.modified = [];
13451 this.fireEvent("clear", this);
13455 * Inserts Records to the Store at the given index and fires the add event.
13456 * @param {Number} index The start index at which to insert the passed Records.
13457 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13459 insert : function(index, records){
13460 records = [].concat(records);
13461 for(var i = 0, len = records.length; i < len; i++){
13462 this.data.insert(index, records[i]);
13463 records[i].join(this);
13465 this.fireEvent("add", this, records, index);
13469 * Get the index within the cache of the passed Record.
13470 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13471 * @return {Number} The index of the passed Record. Returns -1 if not found.
13473 indexOf : function(record){
13474 return this.data.indexOf(record);
13478 * Get the index within the cache of the Record with the passed id.
13479 * @param {String} id The id of the Record to find.
13480 * @return {Number} The index of the Record. Returns -1 if not found.
13482 indexOfId : function(id){
13483 return this.data.indexOfKey(id);
13487 * Get the Record with the specified id.
13488 * @param {String} id The id of the Record to find.
13489 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13491 getById : function(id){
13492 return this.data.key(id);
13496 * Get the Record at the specified index.
13497 * @param {Number} index The index of the Record to find.
13498 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13500 getAt : function(index){
13501 return this.data.itemAt(index);
13505 * Returns a range of Records between specified indices.
13506 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13507 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13508 * @return {Roo.data.Record[]} An array of Records
13510 getRange : function(start, end){
13511 return this.data.getRange(start, end);
13515 storeOptions : function(o){
13516 o = Roo.apply({}, o);
13519 this.lastOptions = o;
13523 * Loads the Record cache from the configured Proxy using the configured Reader.
13525 * If using remote paging, then the first load call must specify the <em>start</em>
13526 * and <em>limit</em> properties in the options.params property to establish the initial
13527 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13529 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13530 * and this call will return before the new data has been loaded. Perform any post-processing
13531 * in a callback function, or in a "load" event handler.</strong>
13533 * @param {Object} options An object containing properties which control loading options:<ul>
13534 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13535 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13536 * passed the following arguments:<ul>
13537 * <li>r : Roo.data.Record[]</li>
13538 * <li>options: Options object from the load call</li>
13539 * <li>success: Boolean success indicator</li></ul></li>
13540 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13541 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13544 load : function(options){
13545 options = options || {};
13546 if(this.fireEvent("beforeload", this, options) !== false){
13547 this.storeOptions(options);
13548 var p = Roo.apply(options.params || {}, this.baseParams);
13549 // if meta was not loaded from remote source.. try requesting it.
13550 if (!this.reader.metaFromRemote) {
13551 p._requestMeta = 1;
13553 if(this.sortInfo && this.remoteSort){
13554 var pn = this.paramNames;
13555 p[pn["sort"]] = this.sortInfo.field;
13556 p[pn["dir"]] = this.sortInfo.direction;
13558 if (this.multiSort) {
13559 var pn = this.paramNames;
13560 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13563 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13568 * Reloads the Record cache from the configured Proxy using the configured Reader and
13569 * the options from the last load operation performed.
13570 * @param {Object} options (optional) An object containing properties which may override the options
13571 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13572 * the most recently used options are reused).
13574 reload : function(options){
13575 this.load(Roo.applyIf(options||{}, this.lastOptions));
13579 // Called as a callback by the Reader during a load operation.
13580 loadRecords : function(o, options, success){
13581 if(!o || success === false){
13582 if(success !== false){
13583 this.fireEvent("load", this, [], options, o);
13585 if(options.callback){
13586 options.callback.call(options.scope || this, [], options, false);
13590 // if data returned failure - throw an exception.
13591 if (o.success === false) {
13592 // show a message if no listener is registered.
13593 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13594 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13596 // loadmask wil be hooked into this..
13597 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13600 var r = o.records, t = o.totalRecords || r.length;
13602 this.fireEvent("beforeloadadd", this, r, options, o);
13604 if(!options || options.add !== true){
13605 if(this.pruneModifiedRecords){
13606 this.modified = [];
13608 for(var i = 0, len = r.length; i < len; i++){
13612 this.data = this.snapshot;
13613 delete this.snapshot;
13616 this.data.addAll(r);
13617 this.totalLength = t;
13619 this.fireEvent("datachanged", this);
13621 this.totalLength = Math.max(t, this.data.length+r.length);
13625 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13627 var e = new Roo.data.Record({});
13629 e.set(this.parent.displayField, this.parent.emptyTitle);
13630 e.set(this.parent.valueField, '');
13635 this.fireEvent("load", this, r, options, o);
13636 if(options.callback){
13637 options.callback.call(options.scope || this, r, options, true);
13643 * Loads data from a passed data block. A Reader which understands the format of the data
13644 * must have been configured in the constructor.
13645 * @param {Object} data The data block from which to read the Records. The format of the data expected
13646 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13647 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13649 loadData : function(o, append){
13650 var r = this.reader.readRecords(o);
13651 this.loadRecords(r, {add: append}, true);
13655 * using 'cn' the nested child reader read the child array into it's child stores.
13656 * @param {Object} rec The record with a 'children array
13658 loadDataFromChildren : function(rec)
13660 this.loadData(this.reader.toLoadData(rec));
13665 * Gets the number of cached records.
13667 * <em>If using paging, this may not be the total size of the dataset. If the data object
13668 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13669 * the data set size</em>
13671 getCount : function(){
13672 return this.data.length || 0;
13676 * Gets the total number of records in the dataset as returned by the server.
13678 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13679 * the dataset size</em>
13681 getTotalCount : function(){
13682 return this.totalLength || 0;
13686 * Returns the sort state of the Store as an object with two properties:
13688 field {String} The name of the field by which the Records are sorted
13689 direction {String} The sort order, "ASC" or "DESC"
13692 getSortState : function(){
13693 return this.sortInfo;
13697 applySort : function(){
13698 if(this.sortInfo && !this.remoteSort){
13699 var s = this.sortInfo, f = s.field;
13700 var st = this.fields.get(f).sortType;
13701 var fn = function(r1, r2){
13702 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13703 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13705 this.data.sort(s.direction, fn);
13706 if(this.snapshot && this.snapshot != this.data){
13707 this.snapshot.sort(s.direction, fn);
13713 * Sets the default sort column and order to be used by the next load operation.
13714 * @param {String} fieldName The name of the field to sort by.
13715 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13717 setDefaultSort : function(field, dir){
13718 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13722 * Sort the Records.
13723 * If remote sorting is used, the sort is performed on the server, and the cache is
13724 * reloaded. If local sorting is used, the cache is sorted internally.
13725 * @param {String} fieldName The name of the field to sort by.
13726 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13728 sort : function(fieldName, dir){
13729 var f = this.fields.get(fieldName);
13731 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13733 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13734 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13739 this.sortToggle[f.name] = dir;
13740 this.sortInfo = {field: f.name, direction: dir};
13741 if(!this.remoteSort){
13743 this.fireEvent("datachanged", this);
13745 this.load(this.lastOptions);
13750 * Calls the specified function for each of the Records in the cache.
13751 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13752 * Returning <em>false</em> aborts and exits the iteration.
13753 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13755 each : function(fn, scope){
13756 this.data.each(fn, scope);
13760 * Gets all records modified since the last commit. Modified records are persisted across load operations
13761 * (e.g., during paging).
13762 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13764 getModifiedRecords : function(){
13765 return this.modified;
13769 createFilterFn : function(property, value, anyMatch){
13770 if(!value.exec){ // not a regex
13771 value = String(value);
13772 if(value.length == 0){
13775 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13777 return function(r){
13778 return value.test(r.data[property]);
13783 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13784 * @param {String} property A field on your records
13785 * @param {Number} start The record index to start at (defaults to 0)
13786 * @param {Number} end The last record index to include (defaults to length - 1)
13787 * @return {Number} The sum
13789 sum : function(property, start, end){
13790 var rs = this.data.items, v = 0;
13791 start = start || 0;
13792 end = (end || end === 0) ? end : rs.length-1;
13794 for(var i = start; i <= end; i++){
13795 v += (rs[i].data[property] || 0);
13801 * Filter the records by a specified property.
13802 * @param {String} field A field on your records
13803 * @param {String/RegExp} value Either a string that the field
13804 * should start with or a RegExp to test against the field
13805 * @param {Boolean} anyMatch True to match any part not just the beginning
13807 filter : function(property, value, anyMatch){
13808 var fn = this.createFilterFn(property, value, anyMatch);
13809 return fn ? this.filterBy(fn) : this.clearFilter();
13813 * Filter by a function. The specified function will be called with each
13814 * record in this data source. If the function returns true the record is included,
13815 * otherwise it is filtered.
13816 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13817 * @param {Object} scope (optional) The scope of the function (defaults to this)
13819 filterBy : function(fn, scope){
13820 this.snapshot = this.snapshot || this.data;
13821 this.data = this.queryBy(fn, scope||this);
13822 this.fireEvent("datachanged", this);
13826 * Query the records by a specified property.
13827 * @param {String} field A field on your records
13828 * @param {String/RegExp} value Either a string that the field
13829 * should start with or a RegExp to test against the field
13830 * @param {Boolean} anyMatch True to match any part not just the beginning
13831 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13833 query : function(property, value, anyMatch){
13834 var fn = this.createFilterFn(property, value, anyMatch);
13835 return fn ? this.queryBy(fn) : this.data.clone();
13839 * Query by a function. The specified function will be called with each
13840 * record in this data source. If the function returns true the record is included
13842 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13843 * @param {Object} scope (optional) The scope of the function (defaults to this)
13844 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13846 queryBy : function(fn, scope){
13847 var data = this.snapshot || this.data;
13848 return data.filterBy(fn, scope||this);
13852 * Collects unique values for a particular dataIndex from this store.
13853 * @param {String} dataIndex The property to collect
13854 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13855 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13856 * @return {Array} An array of the unique values
13858 collect : function(dataIndex, allowNull, bypassFilter){
13859 var d = (bypassFilter === true && this.snapshot) ?
13860 this.snapshot.items : this.data.items;
13861 var v, sv, r = [], l = {};
13862 for(var i = 0, len = d.length; i < len; i++){
13863 v = d[i].data[dataIndex];
13865 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13874 * Revert to a view of the Record cache with no filtering applied.
13875 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13877 clearFilter : function(suppressEvent){
13878 if(this.snapshot && this.snapshot != this.data){
13879 this.data = this.snapshot;
13880 delete this.snapshot;
13881 if(suppressEvent !== true){
13882 this.fireEvent("datachanged", this);
13888 afterEdit : function(record){
13889 if(this.modified.indexOf(record) == -1){
13890 this.modified.push(record);
13892 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13896 afterReject : function(record){
13897 this.modified.remove(record);
13898 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13902 afterCommit : function(record){
13903 this.modified.remove(record);
13904 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13908 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13909 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13911 commitChanges : function(){
13912 var m = this.modified.slice(0);
13913 this.modified = [];
13914 for(var i = 0, len = m.length; i < len; i++){
13920 * Cancel outstanding changes on all changed records.
13922 rejectChanges : function(){
13923 var m = this.modified.slice(0);
13924 this.modified = [];
13925 for(var i = 0, len = m.length; i < len; i++){
13930 onMetaChange : function(meta, rtype, o){
13931 this.recordType = rtype;
13932 this.fields = rtype.prototype.fields;
13933 delete this.snapshot;
13934 this.sortInfo = meta.sortInfo || this.sortInfo;
13935 this.modified = [];
13936 this.fireEvent('metachange', this, this.reader.meta);
13939 moveIndex : function(data, type)
13941 var index = this.indexOf(data);
13943 var newIndex = index + type;
13947 this.insert(newIndex, data);
13952 * Ext JS Library 1.1.1
13953 * Copyright(c) 2006-2007, Ext JS, LLC.
13955 * Originally Released Under LGPL - original licence link has changed is not relivant.
13958 * <script type="text/javascript">
13962 * @class Roo.data.SimpleStore
13963 * @extends Roo.data.Store
13964 * Small helper class to make creating Stores from Array data easier.
13965 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13966 * @cfg {Array} fields An array of field definition objects, or field name strings.
13967 * @cfg {Object} an existing reader (eg. copied from another store)
13968 * @cfg {Array} data The multi-dimensional array of data
13970 * @param {Object} config
13972 Roo.data.SimpleStore = function(config)
13974 Roo.data.SimpleStore.superclass.constructor.call(this, {
13976 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13979 Roo.data.Record.create(config.fields)
13981 proxy : new Roo.data.MemoryProxy(config.data)
13985 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13987 * Ext JS Library 1.1.1
13988 * Copyright(c) 2006-2007, Ext JS, LLC.
13990 * Originally Released Under LGPL - original licence link has changed is not relivant.
13993 * <script type="text/javascript">
13998 * @extends Roo.data.Store
13999 * @class Roo.data.JsonStore
14000 * Small helper class to make creating Stores for JSON data easier. <br/>
14002 var store = new Roo.data.JsonStore({
14003 url: 'get-images.php',
14005 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14008 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14009 * JsonReader and HttpProxy (unless inline data is provided).</b>
14010 * @cfg {Array} fields An array of field definition objects, or field name strings.
14012 * @param {Object} config
14014 Roo.data.JsonStore = function(c){
14015 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14016 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14017 reader: new Roo.data.JsonReader(c, c.fields)
14020 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14022 * Ext JS Library 1.1.1
14023 * Copyright(c) 2006-2007, Ext JS, LLC.
14025 * Originally Released Under LGPL - original licence link has changed is not relivant.
14028 * <script type="text/javascript">
14032 Roo.data.Field = function(config){
14033 if(typeof config == "string"){
14034 config = {name: config};
14036 Roo.apply(this, config);
14039 this.type = "auto";
14042 var st = Roo.data.SortTypes;
14043 // named sortTypes are supported, here we look them up
14044 if(typeof this.sortType == "string"){
14045 this.sortType = st[this.sortType];
14048 // set default sortType for strings and dates
14049 if(!this.sortType){
14052 this.sortType = st.asUCString;
14055 this.sortType = st.asDate;
14058 this.sortType = st.none;
14063 var stripRe = /[\$,%]/g;
14065 // prebuilt conversion function for this field, instead of
14066 // switching every time we're reading a value
14068 var cv, dateFormat = this.dateFormat;
14073 cv = function(v){ return v; };
14076 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14080 return v !== undefined && v !== null && v !== '' ?
14081 parseInt(String(v).replace(stripRe, ""), 10) : '';
14086 return v !== undefined && v !== null && v !== '' ?
14087 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14092 cv = function(v){ return v === true || v === "true" || v == 1; };
14099 if(v instanceof Date){
14103 if(dateFormat == "timestamp"){
14104 return new Date(v*1000);
14106 return Date.parseDate(v, dateFormat);
14108 var parsed = Date.parse(v);
14109 return parsed ? new Date(parsed) : null;
14118 Roo.data.Field.prototype = {
14126 * Ext JS Library 1.1.1
14127 * Copyright(c) 2006-2007, Ext JS, LLC.
14129 * Originally Released Under LGPL - original licence link has changed is not relivant.
14132 * <script type="text/javascript">
14135 // Base class for reading structured data from a data source. This class is intended to be
14136 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14139 * @class Roo.data.DataReader
14140 * Base class for reading structured data from a data source. This class is intended to be
14141 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14144 Roo.data.DataReader = function(meta, recordType){
14148 this.recordType = recordType instanceof Array ?
14149 Roo.data.Record.create(recordType) : recordType;
14152 Roo.data.DataReader.prototype = {
14155 readerType : 'Data',
14157 * Create an empty record
14158 * @param {Object} data (optional) - overlay some values
14159 * @return {Roo.data.Record} record created.
14161 newRow : function(d) {
14163 this.recordType.prototype.fields.each(function(c) {
14165 case 'int' : da[c.name] = 0; break;
14166 case 'date' : da[c.name] = new Date(); break;
14167 case 'float' : da[c.name] = 0.0; break;
14168 case 'boolean' : da[c.name] = false; break;
14169 default : da[c.name] = ""; break;
14173 return new this.recordType(Roo.apply(da, d));
14179 * Ext JS Library 1.1.1
14180 * Copyright(c) 2006-2007, Ext JS, LLC.
14182 * Originally Released Under LGPL - original licence link has changed is not relivant.
14185 * <script type="text/javascript">
14189 * @class Roo.data.DataProxy
14190 * @extends Roo.data.Observable
14191 * This class is an abstract base class for implementations which provide retrieval of
14192 * unformatted data objects.<br>
14194 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14195 * (of the appropriate type which knows how to parse the data object) to provide a block of
14196 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14198 * Custom implementations must implement the load method as described in
14199 * {@link Roo.data.HttpProxy#load}.
14201 Roo.data.DataProxy = function(){
14204 * @event beforeload
14205 * Fires before a network request is made to retrieve a data object.
14206 * @param {Object} This DataProxy object.
14207 * @param {Object} params The params parameter to the load function.
14212 * Fires before the load method's callback is called.
14213 * @param {Object} This DataProxy object.
14214 * @param {Object} o The data object.
14215 * @param {Object} arg The callback argument object passed to the load function.
14219 * @event loadexception
14220 * Fires if an Exception occurs during data retrieval.
14221 * @param {Object} This DataProxy object.
14222 * @param {Object} o The data object.
14223 * @param {Object} arg The callback argument object passed to the load function.
14224 * @param {Object} e The Exception.
14226 loadexception : true
14228 Roo.data.DataProxy.superclass.constructor.call(this);
14231 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14234 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14238 * Ext JS Library 1.1.1
14239 * Copyright(c) 2006-2007, Ext JS, LLC.
14241 * Originally Released Under LGPL - original licence link has changed is not relivant.
14244 * <script type="text/javascript">
14247 * @class Roo.data.MemoryProxy
14248 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14249 * to the Reader when its load method is called.
14251 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14253 Roo.data.MemoryProxy = function(data){
14257 Roo.data.MemoryProxy.superclass.constructor.call(this);
14261 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14264 * Load data from the requested source (in this case an in-memory
14265 * data object passed to the constructor), read the data object into
14266 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14267 * process that block using the passed callback.
14268 * @param {Object} params This parameter is not used by the MemoryProxy class.
14269 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14270 * object into a block of Roo.data.Records.
14271 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14272 * The function must be passed <ul>
14273 * <li>The Record block object</li>
14274 * <li>The "arg" argument from the load function</li>
14275 * <li>A boolean success indicator</li>
14277 * @param {Object} scope The scope in which to call the callback
14278 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14280 load : function(params, reader, callback, scope, arg){
14281 params = params || {};
14284 result = reader.readRecords(params.data ? params.data :this.data);
14286 this.fireEvent("loadexception", this, arg, null, e);
14287 callback.call(scope, null, arg, false);
14290 callback.call(scope, result, arg, true);
14294 update : function(params, records){
14299 * Ext JS Library 1.1.1
14300 * Copyright(c) 2006-2007, Ext JS, LLC.
14302 * Originally Released Under LGPL - original licence link has changed is not relivant.
14305 * <script type="text/javascript">
14308 * @class Roo.data.HttpProxy
14309 * @extends Roo.data.DataProxy
14310 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14311 * configured to reference a certain URL.<br><br>
14313 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14314 * from which the running page was served.<br><br>
14316 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14318 * Be aware that to enable the browser to parse an XML document, the server must set
14319 * the Content-Type header in the HTTP response to "text/xml".
14321 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14322 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14323 * will be used to make the request.
14325 Roo.data.HttpProxy = function(conn){
14326 Roo.data.HttpProxy.superclass.constructor.call(this);
14327 // is conn a conn config or a real conn?
14329 this.useAjax = !conn || !conn.events;
14333 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14334 // thse are take from connection...
14337 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14340 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14341 * extra parameters to each request made by this object. (defaults to undefined)
14344 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14345 * to each request made by this object. (defaults to undefined)
14348 * @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)
14351 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14354 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14360 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14364 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14365 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14366 * a finer-grained basis than the DataProxy events.
14368 getConnection : function(){
14369 return this.useAjax ? Roo.Ajax : this.conn;
14373 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14374 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14375 * process that block using the passed callback.
14376 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14377 * for the request to the remote server.
14378 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14379 * object into a block of Roo.data.Records.
14380 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14381 * The function must be passed <ul>
14382 * <li>The Record block object</li>
14383 * <li>The "arg" argument from the load function</li>
14384 * <li>A boolean success indicator</li>
14386 * @param {Object} scope The scope in which to call the callback
14387 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14389 load : function(params, reader, callback, scope, arg){
14390 if(this.fireEvent("beforeload", this, params) !== false){
14392 params : params || {},
14394 callback : callback,
14399 callback : this.loadResponse,
14403 Roo.applyIf(o, this.conn);
14404 if(this.activeRequest){
14405 Roo.Ajax.abort(this.activeRequest);
14407 this.activeRequest = Roo.Ajax.request(o);
14409 this.conn.request(o);
14412 callback.call(scope||this, null, arg, false);
14417 loadResponse : function(o, success, response){
14418 delete this.activeRequest;
14420 this.fireEvent("loadexception", this, o, response);
14421 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14426 result = o.reader.read(response);
14428 this.fireEvent("loadexception", this, o, response, e);
14429 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14433 this.fireEvent("load", this, o, o.request.arg);
14434 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14438 update : function(dataSet){
14443 updateResponse : function(dataSet){
14448 * Ext JS Library 1.1.1
14449 * Copyright(c) 2006-2007, Ext JS, LLC.
14451 * Originally Released Under LGPL - original licence link has changed is not relivant.
14454 * <script type="text/javascript">
14458 * @class Roo.data.ScriptTagProxy
14459 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14460 * other than the originating domain of the running page.<br><br>
14462 * <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
14463 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14465 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14466 * source code that is used as the source inside a <script> tag.<br><br>
14468 * In order for the browser to process the returned data, the server must wrap the data object
14469 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14470 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14471 * depending on whether the callback name was passed:
14474 boolean scriptTag = false;
14475 String cb = request.getParameter("callback");
14478 response.setContentType("text/javascript");
14480 response.setContentType("application/x-json");
14482 Writer out = response.getWriter();
14484 out.write(cb + "(");
14486 out.print(dataBlock.toJsonString());
14493 * @param {Object} config A configuration object.
14495 Roo.data.ScriptTagProxy = function(config){
14496 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14497 Roo.apply(this, config);
14498 this.head = document.getElementsByTagName("head")[0];
14501 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14503 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14505 * @cfg {String} url The URL from which to request the data object.
14508 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14512 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14513 * the server the name of the callback function set up by the load call to process the returned data object.
14514 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14515 * javascript output which calls this named function passing the data object as its only parameter.
14517 callbackParam : "callback",
14519 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14520 * name to the request.
14525 * Load data from the configured URL, read the data object into
14526 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14527 * process that block using the passed callback.
14528 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14529 * for the request to the remote server.
14530 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14531 * object into a block of Roo.data.Records.
14532 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14533 * The function must be passed <ul>
14534 * <li>The Record block object</li>
14535 * <li>The "arg" argument from the load function</li>
14536 * <li>A boolean success indicator</li>
14538 * @param {Object} scope The scope in which to call the callback
14539 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14541 load : function(params, reader, callback, scope, arg){
14542 if(this.fireEvent("beforeload", this, params) !== false){
14544 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14546 var url = this.url;
14547 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14549 url += "&_dc=" + (new Date().getTime());
14551 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14554 cb : "stcCallback"+transId,
14555 scriptId : "stcScript"+transId,
14559 callback : callback,
14565 window[trans.cb] = function(o){
14566 conn.handleResponse(o, trans);
14569 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14571 if(this.autoAbort !== false){
14575 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14577 var script = document.createElement("script");
14578 script.setAttribute("src", url);
14579 script.setAttribute("type", "text/javascript");
14580 script.setAttribute("id", trans.scriptId);
14581 this.head.appendChild(script);
14583 this.trans = trans;
14585 callback.call(scope||this, null, arg, false);
14590 isLoading : function(){
14591 return this.trans ? true : false;
14595 * Abort the current server request.
14597 abort : function(){
14598 if(this.isLoading()){
14599 this.destroyTrans(this.trans);
14604 destroyTrans : function(trans, isLoaded){
14605 this.head.removeChild(document.getElementById(trans.scriptId));
14606 clearTimeout(trans.timeoutId);
14608 window[trans.cb] = undefined;
14610 delete window[trans.cb];
14613 // if hasn't been loaded, wait for load to remove it to prevent script error
14614 window[trans.cb] = function(){
14615 window[trans.cb] = undefined;
14617 delete window[trans.cb];
14624 handleResponse : function(o, trans){
14625 this.trans = false;
14626 this.destroyTrans(trans, true);
14629 result = trans.reader.readRecords(o);
14631 this.fireEvent("loadexception", this, o, trans.arg, e);
14632 trans.callback.call(trans.scope||window, null, trans.arg, false);
14635 this.fireEvent("load", this, o, trans.arg);
14636 trans.callback.call(trans.scope||window, result, trans.arg, true);
14640 handleFailure : function(trans){
14641 this.trans = false;
14642 this.destroyTrans(trans, false);
14643 this.fireEvent("loadexception", this, null, trans.arg);
14644 trans.callback.call(trans.scope||window, null, trans.arg, false);
14648 * Ext JS Library 1.1.1
14649 * Copyright(c) 2006-2007, Ext JS, LLC.
14651 * Originally Released Under LGPL - original licence link has changed is not relivant.
14654 * <script type="text/javascript">
14658 * @class Roo.data.JsonReader
14659 * @extends Roo.data.DataReader
14660 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14661 * based on mappings in a provided Roo.data.Record constructor.
14663 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14664 * in the reply previously.
14669 var RecordDef = Roo.data.Record.create([
14670 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14671 {name: 'occupation'} // This field will use "occupation" as the mapping.
14673 var myReader = new Roo.data.JsonReader({
14674 totalProperty: "results", // The property which contains the total dataset size (optional)
14675 root: "rows", // The property which contains an Array of row objects
14676 id: "id" // The property within each row object that provides an ID for the record (optional)
14680 * This would consume a JSON file like this:
14682 { 'results': 2, 'rows': [
14683 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14684 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14687 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14688 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14689 * paged from the remote server.
14690 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14691 * @cfg {String} root name of the property which contains the Array of row objects.
14692 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14693 * @cfg {Array} fields Array of field definition objects
14695 * Create a new JsonReader
14696 * @param {Object} meta Metadata configuration options
14697 * @param {Object} recordType Either an Array of field definition objects,
14698 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14700 Roo.data.JsonReader = function(meta, recordType){
14703 // set some defaults:
14704 Roo.applyIf(meta, {
14705 totalProperty: 'total',
14706 successProperty : 'success',
14711 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14713 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14715 readerType : 'Json',
14718 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14719 * Used by Store query builder to append _requestMeta to params.
14722 metaFromRemote : false,
14724 * This method is only used by a DataProxy which has retrieved data from a remote server.
14725 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14726 * @return {Object} data A data block which is used by an Roo.data.Store object as
14727 * a cache of Roo.data.Records.
14729 read : function(response){
14730 var json = response.responseText;
14732 var o = /* eval:var:o */ eval("("+json+")");
14734 throw {message: "JsonReader.read: Json object not found"};
14740 this.metaFromRemote = true;
14741 this.meta = o.metaData;
14742 this.recordType = Roo.data.Record.create(o.metaData.fields);
14743 this.onMetaChange(this.meta, this.recordType, o);
14745 return this.readRecords(o);
14748 // private function a store will implement
14749 onMetaChange : function(meta, recordType, o){
14756 simpleAccess: function(obj, subsc) {
14763 getJsonAccessor: function(){
14765 return function(expr) {
14767 return(re.test(expr))
14768 ? new Function("obj", "return obj." + expr)
14773 return Roo.emptyFn;
14778 * Create a data block containing Roo.data.Records from an XML document.
14779 * @param {Object} o An object which contains an Array of row objects in the property specified
14780 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14781 * which contains the total size of the dataset.
14782 * @return {Object} data A data block which is used by an Roo.data.Store object as
14783 * a cache of Roo.data.Records.
14785 readRecords : function(o){
14787 * After any data loads, the raw JSON data is available for further custom processing.
14791 var s = this.meta, Record = this.recordType,
14792 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14794 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14796 if(s.totalProperty) {
14797 this.getTotal = this.getJsonAccessor(s.totalProperty);
14799 if(s.successProperty) {
14800 this.getSuccess = this.getJsonAccessor(s.successProperty);
14802 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14804 var g = this.getJsonAccessor(s.id);
14805 this.getId = function(rec) {
14807 return (r === undefined || r === "") ? null : r;
14810 this.getId = function(){return null;};
14813 for(var jj = 0; jj < fl; jj++){
14815 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14816 this.ef[jj] = this.getJsonAccessor(map);
14820 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14821 if(s.totalProperty){
14822 var vt = parseInt(this.getTotal(o), 10);
14827 if(s.successProperty){
14828 var vs = this.getSuccess(o);
14829 if(vs === false || vs === 'false'){
14834 for(var i = 0; i < c; i++){
14837 var id = this.getId(n);
14838 for(var j = 0; j < fl; j++){
14840 var v = this.ef[j](n);
14842 Roo.log('missing convert for ' + f.name);
14846 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14848 var record = new Record(values, id);
14850 records[i] = record;
14856 totalRecords : totalRecords
14859 // used when loading children.. @see loadDataFromChildren
14860 toLoadData: function(rec)
14862 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14863 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14864 return { data : data, total : data.length };
14869 * Ext JS Library 1.1.1
14870 * Copyright(c) 2006-2007, Ext JS, LLC.
14872 * Originally Released Under LGPL - original licence link has changed is not relivant.
14875 * <script type="text/javascript">
14879 * @class Roo.data.ArrayReader
14880 * @extends Roo.data.DataReader
14881 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14882 * Each element of that Array represents a row of data fields. The
14883 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14884 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14888 var RecordDef = Roo.data.Record.create([
14889 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14890 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14892 var myReader = new Roo.data.ArrayReader({
14893 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14897 * This would consume an Array like this:
14899 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14903 * Create a new JsonReader
14904 * @param {Object} meta Metadata configuration options.
14905 * @param {Object|Array} recordType Either an Array of field definition objects
14907 * @cfg {Array} fields Array of field definition objects
14908 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14909 * as specified to {@link Roo.data.Record#create},
14910 * or an {@link Roo.data.Record} object
14913 * created using {@link Roo.data.Record#create}.
14915 Roo.data.ArrayReader = function(meta, recordType)
14917 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14920 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14923 * Create a data block containing Roo.data.Records from an XML document.
14924 * @param {Object} o An Array of row objects which represents the dataset.
14925 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14926 * a cache of Roo.data.Records.
14928 readRecords : function(o)
14930 var sid = this.meta ? this.meta.id : null;
14931 var recordType = this.recordType, fields = recordType.prototype.fields;
14934 for(var i = 0; i < root.length; i++){
14937 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14938 for(var j = 0, jlen = fields.length; j < jlen; j++){
14939 var f = fields.items[j];
14940 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14941 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14943 values[f.name] = v;
14945 var record = new recordType(values, id);
14947 records[records.length] = record;
14951 totalRecords : records.length
14954 // used when loading children.. @see loadDataFromChildren
14955 toLoadData: function(rec)
14957 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14958 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14969 * @class Roo.bootstrap.ComboBox
14970 * @extends Roo.bootstrap.TriggerField
14971 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14972 * @cfg {Boolean} append (true|false) default false
14973 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14974 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14975 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14976 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14977 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14978 * @cfg {Boolean} animate default true
14979 * @cfg {Boolean} emptyResultText only for touch device
14980 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14981 * @cfg {String} emptyTitle default ''
14982 * @cfg {Number} width fixed with? experimental
14984 * Create a new ComboBox.
14985 * @param {Object} config Configuration options
14987 Roo.bootstrap.ComboBox = function(config){
14988 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14992 * Fires when the dropdown list is expanded
14993 * @param {Roo.bootstrap.ComboBox} combo This combo box
14998 * Fires when the dropdown list is collapsed
14999 * @param {Roo.bootstrap.ComboBox} combo This combo box
15003 * @event beforeselect
15004 * Fires before a list item is selected. Return false to cancel the selection.
15005 * @param {Roo.bootstrap.ComboBox} combo This combo box
15006 * @param {Roo.data.Record} record The data record returned from the underlying store
15007 * @param {Number} index The index of the selected item in the dropdown list
15009 'beforeselect' : true,
15012 * Fires when a list item is selected
15013 * @param {Roo.bootstrap.ComboBox} combo This combo box
15014 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15015 * @param {Number} index The index of the selected item in the dropdown list
15019 * @event beforequery
15020 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15021 * The event object passed has these properties:
15022 * @param {Roo.bootstrap.ComboBox} combo This combo box
15023 * @param {String} query The query
15024 * @param {Boolean} forceAll true to force "all" query
15025 * @param {Boolean} cancel true to cancel the query
15026 * @param {Object} e The query event object
15028 'beforequery': true,
15031 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15032 * @param {Roo.bootstrap.ComboBox} combo This combo box
15037 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15038 * @param {Roo.bootstrap.ComboBox} combo This combo box
15039 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15044 * Fires when the remove value from the combobox array
15045 * @param {Roo.bootstrap.ComboBox} combo This combo box
15049 * @event afterremove
15050 * Fires when the remove value from the combobox array
15051 * @param {Roo.bootstrap.ComboBox} combo This combo box
15053 'afterremove' : true,
15055 * @event specialfilter
15056 * Fires when specialfilter
15057 * @param {Roo.bootstrap.ComboBox} combo This combo box
15059 'specialfilter' : true,
15062 * Fires when tick the element
15063 * @param {Roo.bootstrap.ComboBox} combo This combo box
15067 * @event touchviewdisplay
15068 * Fires when touch view require special display (default is using displayField)
15069 * @param {Roo.bootstrap.ComboBox} combo This combo box
15070 * @param {Object} cfg set html .
15072 'touchviewdisplay' : true
15077 this.tickItems = [];
15079 this.selectedIndex = -1;
15080 if(this.mode == 'local'){
15081 if(config.queryDelay === undefined){
15082 this.queryDelay = 10;
15084 if(config.minChars === undefined){
15090 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15093 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15094 * rendering into an Roo.Editor, defaults to false)
15097 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15098 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15101 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15104 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15105 * the dropdown list (defaults to undefined, with no header element)
15109 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15113 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15115 listWidth: undefined,
15117 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15118 * mode = 'remote' or 'text' if mode = 'local')
15120 displayField: undefined,
15123 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15124 * mode = 'remote' or 'value' if mode = 'local').
15125 * Note: use of a valueField requires the user make a selection
15126 * in order for a value to be mapped.
15128 valueField: undefined,
15130 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15135 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15136 * field's data value (defaults to the underlying DOM element's name)
15138 hiddenName: undefined,
15140 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15144 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15146 selectedClass: 'active',
15149 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15153 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15154 * anchor positions (defaults to 'tl-bl')
15156 listAlign: 'tl-bl?',
15158 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15162 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15163 * query specified by the allQuery config option (defaults to 'query')
15165 triggerAction: 'query',
15167 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15168 * (defaults to 4, does not apply if editable = false)
15172 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15173 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15177 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15178 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15182 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15183 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15187 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15188 * when editable = true (defaults to false)
15190 selectOnFocus:false,
15192 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15194 queryParam: 'query',
15196 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15197 * when mode = 'remote' (defaults to 'Loading...')
15199 loadingText: 'Loading...',
15201 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15205 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15209 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15210 * traditional select (defaults to true)
15214 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15218 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15222 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15223 * listWidth has a higher value)
15227 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15228 * allow the user to set arbitrary text into the field (defaults to false)
15230 forceSelection:false,
15232 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15233 * if typeAhead = true (defaults to 250)
15235 typeAheadDelay : 250,
15237 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15238 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15240 valueNotFoundText : undefined,
15242 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15244 blockFocus : false,
15247 * @cfg {Boolean} disableClear Disable showing of clear button.
15249 disableClear : false,
15251 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15253 alwaysQuery : false,
15256 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15261 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15263 invalidClass : "has-warning",
15266 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15268 validClass : "has-success",
15271 * @cfg {Boolean} specialFilter (true|false) special filter default false
15273 specialFilter : false,
15276 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15278 mobileTouchView : true,
15281 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15283 useNativeIOS : false,
15286 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15288 mobile_restrict_height : false,
15290 ios_options : false,
15302 btnPosition : 'right',
15303 triggerList : true,
15304 showToggleBtn : true,
15306 emptyResultText: 'Empty',
15307 triggerText : 'Select',
15311 // element that contains real text value.. (when hidden is used..)
15313 getAutoCreate : function()
15318 * Render classic select for iso
15321 if(Roo.isIOS && this.useNativeIOS){
15322 cfg = this.getAutoCreateNativeIOS();
15330 if(Roo.isTouch && this.mobileTouchView){
15331 cfg = this.getAutoCreateTouchView();
15338 if(!this.tickable){
15339 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15344 * ComboBox with tickable selections
15347 var align = this.labelAlign || this.parentLabelAlign();
15350 cls : 'form-group roo-combobox-tickable' //input-group
15353 var btn_text_select = '';
15354 var btn_text_done = '';
15355 var btn_text_cancel = '';
15357 if (this.btn_text_show) {
15358 btn_text_select = 'Select';
15359 btn_text_done = 'Done';
15360 btn_text_cancel = 'Cancel';
15365 cls : 'tickable-buttons',
15370 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15371 //html : this.triggerText
15372 html: btn_text_select
15378 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15380 html: btn_text_done
15386 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15388 html: btn_text_cancel
15394 buttons.cn.unshift({
15396 cls: 'roo-select2-search-field-input'
15402 Roo.each(buttons.cn, function(c){
15404 c.cls += ' btn-' + _this.size;
15407 if (_this.disabled) {
15414 style : 'display: contents',
15419 cls: 'form-hidden-field'
15423 cls: 'roo-select2-choices',
15427 cls: 'roo-select2-search-field',
15438 cls: 'roo-select2-container input-group roo-select2-container-multi',
15444 // cls: 'typeahead typeahead-long dropdown-menu',
15445 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15450 if(this.hasFeedback && !this.allowBlank){
15454 cls: 'glyphicon form-control-feedback'
15457 combobox.cn.push(feedback);
15464 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15465 tooltip : 'This field is required'
15467 if (Roo.bootstrap.version == 4) {
15470 style : 'display:none'
15473 if (align ==='left' && this.fieldLabel.length) {
15475 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15482 cls : 'control-label col-form-label',
15483 html : this.fieldLabel
15495 var labelCfg = cfg.cn[1];
15496 var contentCfg = cfg.cn[2];
15499 if(this.indicatorpos == 'right'){
15505 cls : 'control-label col-form-label',
15509 html : this.fieldLabel
15525 labelCfg = cfg.cn[0];
15526 contentCfg = cfg.cn[1];
15530 if(this.labelWidth > 12){
15531 labelCfg.style = "width: " + this.labelWidth + 'px';
15533 if(this.width * 1 > 0){
15534 contentCfg.style = "width: " + this.width + 'px';
15536 if(this.labelWidth < 13 && this.labelmd == 0){
15537 this.labelmd = this.labelWidth;
15540 if(this.labellg > 0){
15541 labelCfg.cls += ' col-lg-' + this.labellg;
15542 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15545 if(this.labelmd > 0){
15546 labelCfg.cls += ' col-md-' + this.labelmd;
15547 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15550 if(this.labelsm > 0){
15551 labelCfg.cls += ' col-sm-' + this.labelsm;
15552 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15555 if(this.labelxs > 0){
15556 labelCfg.cls += ' col-xs-' + this.labelxs;
15557 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15561 } else if ( this.fieldLabel.length) {
15562 // Roo.log(" label");
15567 //cls : 'input-group-addon',
15568 html : this.fieldLabel
15573 if(this.indicatorpos == 'right'){
15577 //cls : 'input-group-addon',
15578 html : this.fieldLabel
15588 // Roo.log(" no label && no align");
15595 ['xs','sm','md','lg'].map(function(size){
15596 if (settings[size]) {
15597 cfg.cls += ' col-' + size + '-' + settings[size];
15605 _initEventsCalled : false,
15608 initEvents: function()
15610 if (this._initEventsCalled) { // as we call render... prevent looping...
15613 this._initEventsCalled = true;
15616 throw "can not find store for combo";
15619 this.indicator = this.indicatorEl();
15621 this.store = Roo.factory(this.store, Roo.data);
15622 this.store.parent = this;
15624 // if we are building from html. then this element is so complex, that we can not really
15625 // use the rendered HTML.
15626 // so we have to trash and replace the previous code.
15627 if (Roo.XComponent.build_from_html) {
15628 // remove this element....
15629 var e = this.el.dom, k=0;
15630 while (e ) { e = e.previousSibling; ++k;}
15635 this.rendered = false;
15637 this.render(this.parent().getChildContainer(true), k);
15640 if(Roo.isIOS && this.useNativeIOS){
15641 this.initIOSView();
15649 if(Roo.isTouch && this.mobileTouchView){
15650 this.initTouchView();
15655 this.initTickableEvents();
15659 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15661 if(this.hiddenName){
15663 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15665 this.hiddenField.dom.value =
15666 this.hiddenValue !== undefined ? this.hiddenValue :
15667 this.value !== undefined ? this.value : '';
15669 // prevent input submission
15670 this.el.dom.removeAttribute('name');
15671 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15676 // this.el.dom.setAttribute('autocomplete', 'off');
15679 var cls = 'x-combo-list';
15681 //this.list = new Roo.Layer({
15682 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15688 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15689 _this.list.setWidth(lw);
15692 this.list.on('mouseover', this.onViewOver, this);
15693 this.list.on('mousemove', this.onViewMove, this);
15694 this.list.on('scroll', this.onViewScroll, this);
15697 this.list.swallowEvent('mousewheel');
15698 this.assetHeight = 0;
15701 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15702 this.assetHeight += this.header.getHeight();
15705 this.innerList = this.list.createChild({cls:cls+'-inner'});
15706 this.innerList.on('mouseover', this.onViewOver, this);
15707 this.innerList.on('mousemove', this.onViewMove, this);
15708 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15710 if(this.allowBlank && !this.pageSize && !this.disableClear){
15711 this.footer = this.list.createChild({cls:cls+'-ft'});
15712 this.pageTb = new Roo.Toolbar(this.footer);
15716 this.footer = this.list.createChild({cls:cls+'-ft'});
15717 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15718 {pageSize: this.pageSize});
15722 if (this.pageTb && this.allowBlank && !this.disableClear) {
15724 this.pageTb.add(new Roo.Toolbar.Fill(), {
15725 cls: 'x-btn-icon x-btn-clear',
15727 handler: function()
15730 _this.clearValue();
15731 _this.onSelect(false, -1);
15736 this.assetHeight += this.footer.getHeight();
15741 this.tpl = Roo.bootstrap.version == 4 ?
15742 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15743 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15746 this.view = new Roo.View(this.list, this.tpl, {
15747 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15749 //this.view.wrapEl.setDisplayed(false);
15750 this.view.on('click', this.onViewClick, this);
15753 this.store.on('beforeload', this.onBeforeLoad, this);
15754 this.store.on('load', this.onLoad, this);
15755 this.store.on('loadexception', this.onLoadException, this);
15757 if(this.resizable){
15758 this.resizer = new Roo.Resizable(this.list, {
15759 pinned:true, handles:'se'
15761 this.resizer.on('resize', function(r, w, h){
15762 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15763 this.listWidth = w;
15764 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15765 this.restrictHeight();
15767 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15770 if(!this.editable){
15771 this.editable = true;
15772 this.setEditable(false);
15777 if (typeof(this.events.add.listeners) != 'undefined') {
15779 this.addicon = this.wrap.createChild(
15780 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15782 this.addicon.on('click', function(e) {
15783 this.fireEvent('add', this);
15786 if (typeof(this.events.edit.listeners) != 'undefined') {
15788 this.editicon = this.wrap.createChild(
15789 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15790 if (this.addicon) {
15791 this.editicon.setStyle('margin-left', '40px');
15793 this.editicon.on('click', function(e) {
15795 // we fire even if inothing is selected..
15796 this.fireEvent('edit', this, this.lastData );
15802 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15803 "up" : function(e){
15804 this.inKeyMode = true;
15808 "down" : function(e){
15809 if(!this.isExpanded()){
15810 this.onTriggerClick();
15812 this.inKeyMode = true;
15817 "enter" : function(e){
15818 // this.onViewClick();
15822 if(this.fireEvent("specialkey", this, e)){
15823 this.onViewClick(false);
15829 "esc" : function(e){
15833 "tab" : function(e){
15836 if(this.fireEvent("specialkey", this, e)){
15837 this.onViewClick(false);
15845 doRelay : function(foo, bar, hname){
15846 if(hname == 'down' || this.scope.isExpanded()){
15847 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15856 this.queryDelay = Math.max(this.queryDelay || 10,
15857 this.mode == 'local' ? 10 : 250);
15860 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15862 if(this.typeAhead){
15863 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15865 if(this.editable !== false){
15866 this.inputEl().on("keyup", this.onKeyUp, this);
15868 if(this.forceSelection){
15869 this.inputEl().on('blur', this.doForce, this);
15873 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15874 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15878 initTickableEvents: function()
15882 if(this.hiddenName){
15884 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15886 this.hiddenField.dom.value =
15887 this.hiddenValue !== undefined ? this.hiddenValue :
15888 this.value !== undefined ? this.value : '';
15890 // prevent input submission
15891 this.el.dom.removeAttribute('name');
15892 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15897 // this.list = this.el.select('ul.dropdown-menu',true).first();
15899 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15900 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15901 if(this.triggerList){
15902 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15905 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15906 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15908 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15909 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15911 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15912 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15914 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15915 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15916 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15919 this.cancelBtn.hide();
15924 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15925 _this.list.setWidth(lw);
15928 this.list.on('mouseover', this.onViewOver, this);
15929 this.list.on('mousemove', this.onViewMove, this);
15931 this.list.on('scroll', this.onViewScroll, this);
15934 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15935 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15938 this.view = new Roo.View(this.list, this.tpl, {
15943 selectedClass: this.selectedClass
15946 //this.view.wrapEl.setDisplayed(false);
15947 this.view.on('click', this.onViewClick, this);
15951 this.store.on('beforeload', this.onBeforeLoad, this);
15952 this.store.on('load', this.onLoad, this);
15953 this.store.on('loadexception', this.onLoadException, this);
15956 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15957 "up" : function(e){
15958 this.inKeyMode = true;
15962 "down" : function(e){
15963 this.inKeyMode = true;
15967 "enter" : function(e){
15968 if(this.fireEvent("specialkey", this, e)){
15969 this.onViewClick(false);
15975 "esc" : function(e){
15976 this.onTickableFooterButtonClick(e, false, false);
15979 "tab" : function(e){
15980 this.fireEvent("specialkey", this, e);
15982 this.onTickableFooterButtonClick(e, false, false);
15989 doRelay : function(e, fn, key){
15990 if(this.scope.isExpanded()){
15991 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16000 this.queryDelay = Math.max(this.queryDelay || 10,
16001 this.mode == 'local' ? 10 : 250);
16004 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16006 if(this.typeAhead){
16007 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16010 if(this.editable !== false){
16011 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16014 this.indicator = this.indicatorEl();
16016 if(this.indicator){
16017 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16018 this.indicator.hide();
16023 onDestroy : function(){
16025 this.view.setStore(null);
16026 this.view.el.removeAllListeners();
16027 this.view.el.remove();
16028 this.view.purgeListeners();
16031 this.list.dom.innerHTML = '';
16035 this.store.un('beforeload', this.onBeforeLoad, this);
16036 this.store.un('load', this.onLoad, this);
16037 this.store.un('loadexception', this.onLoadException, this);
16039 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16043 fireKey : function(e){
16044 if(e.isNavKeyPress() && !this.list.isVisible()){
16045 this.fireEvent("specialkey", this, e);
16050 onResize: function(w, h)
16054 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16056 // if(typeof w != 'number'){
16057 // // we do not handle it!?!?
16060 // var tw = this.trigger.getWidth();
16061 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16062 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16064 // this.inputEl().setWidth( this.adjustWidth('input', x));
16066 // //this.trigger.setStyle('left', x+'px');
16068 // if(this.list && this.listWidth === undefined){
16069 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16070 // this.list.setWidth(lw);
16071 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16079 * Allow or prevent the user from directly editing the field text. If false is passed,
16080 * the user will only be able to select from the items defined in the dropdown list. This method
16081 * is the runtime equivalent of setting the 'editable' config option at config time.
16082 * @param {Boolean} value True to allow the user to directly edit the field text
16084 setEditable : function(value){
16085 if(value == this.editable){
16088 this.editable = value;
16090 this.inputEl().dom.setAttribute('readOnly', true);
16091 this.inputEl().on('mousedown', this.onTriggerClick, this);
16092 this.inputEl().addClass('x-combo-noedit');
16094 this.inputEl().dom.setAttribute('readOnly', false);
16095 this.inputEl().un('mousedown', this.onTriggerClick, this);
16096 this.inputEl().removeClass('x-combo-noedit');
16102 onBeforeLoad : function(combo,opts){
16103 if(!this.hasFocus){
16107 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16109 this.restrictHeight();
16110 this.selectedIndex = -1;
16114 onLoad : function(){
16116 this.hasQuery = false;
16118 if(!this.hasFocus){
16122 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16123 this.loading.hide();
16126 if(this.store.getCount() > 0){
16129 this.restrictHeight();
16130 if(this.lastQuery == this.allQuery){
16131 if(this.editable && !this.tickable){
16132 this.inputEl().dom.select();
16136 !this.selectByValue(this.value, true) &&
16139 !this.store.lastOptions ||
16140 typeof(this.store.lastOptions.add) == 'undefined' ||
16141 this.store.lastOptions.add != true
16144 this.select(0, true);
16147 if(this.autoFocus){
16150 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16151 this.taTask.delay(this.typeAheadDelay);
16155 this.onEmptyResults();
16161 onLoadException : function()
16163 this.hasQuery = false;
16165 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16166 this.loading.hide();
16169 if(this.tickable && this.editable){
16174 // only causes errors at present
16175 //Roo.log(this.store.reader.jsonData);
16176 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16178 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16184 onTypeAhead : function(){
16185 if(this.store.getCount() > 0){
16186 var r = this.store.getAt(0);
16187 var newValue = r.data[this.displayField];
16188 var len = newValue.length;
16189 var selStart = this.getRawValue().length;
16191 if(selStart != len){
16192 this.setRawValue(newValue);
16193 this.selectText(selStart, newValue.length);
16199 onSelect : function(record, index){
16201 if(this.fireEvent('beforeselect', this, record, index) !== false){
16203 this.setFromData(index > -1 ? record.data : false);
16206 this.fireEvent('select', this, record, index);
16211 * Returns the currently selected field value or empty string if no value is set.
16212 * @return {String} value The selected value
16214 getValue : function()
16216 if(Roo.isIOS && this.useNativeIOS){
16217 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16221 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16224 if(this.valueField){
16225 return typeof this.value != 'undefined' ? this.value : '';
16227 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16231 getRawValue : function()
16233 if(Roo.isIOS && this.useNativeIOS){
16234 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16237 var v = this.inputEl().getValue();
16243 * Clears any text/value currently set in the field
16245 clearValue : function(){
16247 if(this.hiddenField){
16248 this.hiddenField.dom.value = '';
16251 this.setRawValue('');
16252 this.lastSelectionText = '';
16253 this.lastData = false;
16255 var close = this.closeTriggerEl();
16266 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16267 * will be displayed in the field. If the value does not match the data value of an existing item,
16268 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16269 * Otherwise the field will be blank (although the value will still be set).
16270 * @param {String} value The value to match
16272 setValue : function(v)
16274 if(Roo.isIOS && this.useNativeIOS){
16275 this.setIOSValue(v);
16285 if(this.valueField){
16286 var r = this.findRecord(this.valueField, v);
16288 text = r.data[this.displayField];
16289 }else if(this.valueNotFoundText !== undefined){
16290 text = this.valueNotFoundText;
16293 this.lastSelectionText = text;
16294 if(this.hiddenField){
16295 this.hiddenField.dom.value = v;
16297 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16300 var close = this.closeTriggerEl();
16303 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16309 * @property {Object} the last set data for the element
16314 * Sets the value of the field based on a object which is related to the record format for the store.
16315 * @param {Object} value the value to set as. or false on reset?
16317 setFromData : function(o){
16324 var dv = ''; // display value
16325 var vv = ''; // value value..
16327 if (this.displayField) {
16328 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16330 // this is an error condition!!!
16331 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16334 if(this.valueField){
16335 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16338 var close = this.closeTriggerEl();
16341 if(dv.length || vv * 1 > 0){
16343 this.blockFocus=true;
16349 if(this.hiddenField){
16350 this.hiddenField.dom.value = vv;
16352 this.lastSelectionText = dv;
16353 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16357 // no hidden field.. - we store the value in 'value', but still display
16358 // display field!!!!
16359 this.lastSelectionText = dv;
16360 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16367 reset : function(){
16368 // overridden so that last data is reset..
16375 this.setValue(this.originalValue);
16376 //this.clearInvalid();
16377 this.lastData = false;
16379 this.view.clearSelections();
16385 findRecord : function(prop, value){
16387 if(this.store.getCount() > 0){
16388 this.store.each(function(r){
16389 if(r.data[prop] == value){
16399 getName: function()
16401 // returns hidden if it's set..
16402 if (!this.rendered) {return ''};
16403 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16407 onViewMove : function(e, t){
16408 this.inKeyMode = false;
16412 onViewOver : function(e, t){
16413 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16416 var item = this.view.findItemFromChild(t);
16419 var index = this.view.indexOf(item);
16420 this.select(index, false);
16425 onViewClick : function(view, doFocus, el, e)
16427 var index = this.view.getSelectedIndexes()[0];
16429 var r = this.store.getAt(index);
16433 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16440 Roo.each(this.tickItems, function(v,k){
16442 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16444 _this.tickItems.splice(k, 1);
16446 if(typeof(e) == 'undefined' && view == false){
16447 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16459 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16460 this.tickItems.push(r.data);
16463 if(typeof(e) == 'undefined' && view == false){
16464 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16471 this.onSelect(r, index);
16473 if(doFocus !== false && !this.blockFocus){
16474 this.inputEl().focus();
16479 restrictHeight : function(){
16480 //this.innerList.dom.style.height = '';
16481 //var inner = this.innerList.dom;
16482 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16483 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16484 //this.list.beginUpdate();
16485 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16486 this.list.alignTo(this.inputEl(), this.listAlign);
16487 this.list.alignTo(this.inputEl(), this.listAlign);
16488 //this.list.endUpdate();
16492 onEmptyResults : function(){
16494 if(this.tickable && this.editable){
16495 this.hasFocus = false;
16496 this.restrictHeight();
16504 * Returns true if the dropdown list is expanded, else false.
16506 isExpanded : function(){
16507 return this.list.isVisible();
16511 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16512 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16513 * @param {String} value The data value of the item to select
16514 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16515 * selected item if it is not currently in view (defaults to true)
16516 * @return {Boolean} True if the value matched an item in the list, else false
16518 selectByValue : function(v, scrollIntoView){
16519 if(v !== undefined && v !== null){
16520 var r = this.findRecord(this.valueField || this.displayField, v);
16522 this.select(this.store.indexOf(r), scrollIntoView);
16530 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16531 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16532 * @param {Number} index The zero-based index of the list item to select
16533 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16534 * selected item if it is not currently in view (defaults to true)
16536 select : function(index, scrollIntoView){
16537 this.selectedIndex = index;
16538 this.view.select(index);
16539 if(scrollIntoView !== false){
16540 var el = this.view.getNode(index);
16542 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16545 this.list.scrollChildIntoView(el, false);
16551 selectNext : function(){
16552 var ct = this.store.getCount();
16554 if(this.selectedIndex == -1){
16556 }else if(this.selectedIndex < ct-1){
16557 this.select(this.selectedIndex+1);
16563 selectPrev : function(){
16564 var ct = this.store.getCount();
16566 if(this.selectedIndex == -1){
16568 }else if(this.selectedIndex != 0){
16569 this.select(this.selectedIndex-1);
16575 onKeyUp : function(e){
16576 if(this.editable !== false && !e.isSpecialKey()){
16577 this.lastKey = e.getKey();
16578 this.dqTask.delay(this.queryDelay);
16583 validateBlur : function(){
16584 return !this.list || !this.list.isVisible();
16588 initQuery : function(){
16590 var v = this.getRawValue();
16592 if(this.tickable && this.editable){
16593 v = this.tickableInputEl().getValue();
16600 doForce : function(){
16601 if(this.inputEl().dom.value.length > 0){
16602 this.inputEl().dom.value =
16603 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16609 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16610 * query allowing the query action to be canceled if needed.
16611 * @param {String} query The SQL query to execute
16612 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16613 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16614 * saved in the current store (defaults to false)
16616 doQuery : function(q, forceAll){
16618 if(q === undefined || q === null){
16623 forceAll: forceAll,
16627 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16632 forceAll = qe.forceAll;
16633 if(forceAll === true || (q.length >= this.minChars)){
16635 this.hasQuery = true;
16637 if(this.lastQuery != q || this.alwaysQuery){
16638 this.lastQuery = q;
16639 if(this.mode == 'local'){
16640 this.selectedIndex = -1;
16642 this.store.clearFilter();
16645 if(this.specialFilter){
16646 this.fireEvent('specialfilter', this);
16651 this.store.filter(this.displayField, q);
16654 this.store.fireEvent("datachanged", this.store);
16661 this.store.baseParams[this.queryParam] = q;
16663 var options = {params : this.getParams(q)};
16666 options.add = true;
16667 options.params.start = this.page * this.pageSize;
16670 this.store.load(options);
16673 * this code will make the page width larger, at the beginning, the list not align correctly,
16674 * we should expand the list on onLoad
16675 * so command out it
16680 this.selectedIndex = -1;
16685 this.loadNext = false;
16689 getParams : function(q){
16691 //p[this.queryParam] = q;
16695 p.limit = this.pageSize;
16701 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16703 collapse : function(){
16704 if(!this.isExpanded()){
16710 this.hasFocus = false;
16714 this.cancelBtn.hide();
16715 this.trigger.show();
16718 this.tickableInputEl().dom.value = '';
16719 this.tickableInputEl().blur();
16724 Roo.get(document).un('mousedown', this.collapseIf, this);
16725 Roo.get(document).un('mousewheel', this.collapseIf, this);
16726 if (!this.editable) {
16727 Roo.get(document).un('keydown', this.listKeyPress, this);
16729 this.fireEvent('collapse', this);
16735 collapseIf : function(e){
16736 var in_combo = e.within(this.el);
16737 var in_list = e.within(this.list);
16738 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16740 if (in_combo || in_list || is_list) {
16741 //e.stopPropagation();
16746 this.onTickableFooterButtonClick(e, false, false);
16754 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16756 expand : function(){
16758 if(this.isExpanded() || !this.hasFocus){
16762 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16763 this.list.setWidth(lw);
16769 this.restrictHeight();
16773 this.tickItems = Roo.apply([], this.item);
16776 this.cancelBtn.show();
16777 this.trigger.hide();
16780 this.tickableInputEl().focus();
16785 Roo.get(document).on('mousedown', this.collapseIf, this);
16786 Roo.get(document).on('mousewheel', this.collapseIf, this);
16787 if (!this.editable) {
16788 Roo.get(document).on('keydown', this.listKeyPress, this);
16791 this.fireEvent('expand', this);
16795 // Implements the default empty TriggerField.onTriggerClick function
16796 onTriggerClick : function(e)
16798 Roo.log('trigger click');
16800 if(this.disabled || !this.triggerList){
16805 this.loadNext = false;
16807 if(this.isExpanded()){
16809 if (!this.blockFocus) {
16810 this.inputEl().focus();
16814 this.hasFocus = true;
16815 if(this.triggerAction == 'all') {
16816 this.doQuery(this.allQuery, true);
16818 this.doQuery(this.getRawValue());
16820 if (!this.blockFocus) {
16821 this.inputEl().focus();
16826 onTickableTriggerClick : function(e)
16833 this.loadNext = false;
16834 this.hasFocus = true;
16836 if(this.triggerAction == 'all') {
16837 this.doQuery(this.allQuery, true);
16839 this.doQuery(this.getRawValue());
16843 onSearchFieldClick : function(e)
16845 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16846 this.onTickableFooterButtonClick(e, false, false);
16850 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16855 this.loadNext = false;
16856 this.hasFocus = true;
16858 if(this.triggerAction == 'all') {
16859 this.doQuery(this.allQuery, true);
16861 this.doQuery(this.getRawValue());
16865 listKeyPress : function(e)
16867 //Roo.log('listkeypress');
16868 // scroll to first matching element based on key pres..
16869 if (e.isSpecialKey()) {
16872 var k = String.fromCharCode(e.getKey()).toUpperCase();
16875 var csel = this.view.getSelectedNodes();
16876 var cselitem = false;
16878 var ix = this.view.indexOf(csel[0]);
16879 cselitem = this.store.getAt(ix);
16880 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16886 this.store.each(function(v) {
16888 // start at existing selection.
16889 if (cselitem.id == v.id) {
16895 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16896 match = this.store.indexOf(v);
16902 if (match === false) {
16903 return true; // no more action?
16906 this.view.select(match);
16907 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16908 sn.scrollIntoView(sn.dom.parentNode, false);
16911 onViewScroll : function(e, t){
16913 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){
16917 this.hasQuery = true;
16919 this.loading = this.list.select('.loading', true).first();
16921 if(this.loading === null){
16922 this.list.createChild({
16924 cls: 'loading roo-select2-more-results roo-select2-active',
16925 html: 'Loading more results...'
16928 this.loading = this.list.select('.loading', true).first();
16930 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16932 this.loading.hide();
16935 this.loading.show();
16940 this.loadNext = true;
16942 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16947 addItem : function(o)
16949 var dv = ''; // display value
16951 if (this.displayField) {
16952 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16954 // this is an error condition!!!
16955 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16962 var choice = this.choices.createChild({
16964 cls: 'roo-select2-search-choice',
16973 cls: 'roo-select2-search-choice-close fa fa-times',
16978 }, this.searchField);
16980 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16982 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16990 this.inputEl().dom.value = '';
16995 onRemoveItem : function(e, _self, o)
16997 e.preventDefault();
16999 this.lastItem = Roo.apply([], this.item);
17001 var index = this.item.indexOf(o.data) * 1;
17004 Roo.log('not this item?!');
17008 this.item.splice(index, 1);
17013 this.fireEvent('remove', this, e);
17019 syncValue : function()
17021 if(!this.item.length){
17028 Roo.each(this.item, function(i){
17029 if(_this.valueField){
17030 value.push(i[_this.valueField]);
17037 this.value = value.join(',');
17039 if(this.hiddenField){
17040 this.hiddenField.dom.value = this.value;
17043 this.store.fireEvent("datachanged", this.store);
17048 clearItem : function()
17050 if(!this.multiple){
17056 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17064 if(this.tickable && !Roo.isTouch){
17065 this.view.refresh();
17069 inputEl: function ()
17071 if(Roo.isIOS && this.useNativeIOS){
17072 return this.el.select('select.roo-ios-select', true).first();
17075 if(Roo.isTouch && this.mobileTouchView){
17076 return this.el.select('input.form-control',true).first();
17080 return this.searchField;
17083 return this.el.select('input.form-control',true).first();
17086 onTickableFooterButtonClick : function(e, btn, el)
17088 e.preventDefault();
17090 this.lastItem = Roo.apply([], this.item);
17092 if(btn && btn.name == 'cancel'){
17093 this.tickItems = Roo.apply([], this.item);
17102 Roo.each(this.tickItems, function(o){
17110 validate : function()
17112 if(this.getVisibilityEl().hasClass('hidden')){
17116 var v = this.getRawValue();
17119 v = this.getValue();
17122 if(this.disabled || this.allowBlank || v.length){
17127 this.markInvalid();
17131 tickableInputEl : function()
17133 if(!this.tickable || !this.editable){
17134 return this.inputEl();
17137 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17141 getAutoCreateTouchView : function()
17146 cls: 'form-group' //input-group
17152 type : this.inputType,
17153 cls : 'form-control x-combo-noedit',
17154 autocomplete: 'new-password',
17155 placeholder : this.placeholder || '',
17160 input.name = this.name;
17164 input.cls += ' input-' + this.size;
17167 if (this.disabled) {
17168 input.disabled = true;
17172 cls : 'roo-combobox-wrap',
17179 inputblock.cls += ' input-group';
17181 inputblock.cn.unshift({
17183 cls : 'input-group-addon input-group-prepend input-group-text',
17188 if(this.removable && !this.multiple){
17189 inputblock.cls += ' roo-removable';
17191 inputblock.cn.push({
17194 cls : 'roo-combo-removable-btn close'
17198 if(this.hasFeedback && !this.allowBlank){
17200 inputblock.cls += ' has-feedback';
17202 inputblock.cn.push({
17204 cls: 'glyphicon form-control-feedback'
17211 inputblock.cls += (this.before) ? '' : ' input-group';
17213 inputblock.cn.push({
17215 cls : 'input-group-addon input-group-append input-group-text',
17221 var ibwrap = inputblock;
17226 cls: 'roo-select2-choices',
17230 cls: 'roo-select2-search-field',
17243 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17248 cls: 'form-hidden-field'
17254 if(!this.multiple && this.showToggleBtn){
17260 if (this.caret != false) {
17263 cls: 'fa fa-' + this.caret
17270 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17272 Roo.bootstrap.version == 3 ? caret : '',
17275 cls: 'combobox-clear',
17289 combobox.cls += ' roo-select2-container-multi';
17292 var align = this.labelAlign || this.parentLabelAlign();
17294 if (align ==='left' && this.fieldLabel.length) {
17299 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17300 tooltip : 'This field is required'
17304 cls : 'control-label col-form-label',
17305 html : this.fieldLabel
17309 cls : 'roo-combobox-wrap ',
17316 var labelCfg = cfg.cn[1];
17317 var contentCfg = cfg.cn[2];
17320 if(this.indicatorpos == 'right'){
17325 cls : 'control-label col-form-label',
17329 html : this.fieldLabel
17333 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17334 tooltip : 'This field is required'
17339 cls : "roo-combobox-wrap ",
17347 labelCfg = cfg.cn[0];
17348 contentCfg = cfg.cn[1];
17353 if(this.labelWidth > 12){
17354 labelCfg.style = "width: " + this.labelWidth + 'px';
17357 if(this.labelWidth < 13 && this.labelmd == 0){
17358 this.labelmd = this.labelWidth;
17361 if(this.labellg > 0){
17362 labelCfg.cls += ' col-lg-' + this.labellg;
17363 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17366 if(this.labelmd > 0){
17367 labelCfg.cls += ' col-md-' + this.labelmd;
17368 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17371 if(this.labelsm > 0){
17372 labelCfg.cls += ' col-sm-' + this.labelsm;
17373 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17376 if(this.labelxs > 0){
17377 labelCfg.cls += ' col-xs-' + this.labelxs;
17378 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17382 } else if ( this.fieldLabel.length) {
17386 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17387 tooltip : 'This field is required'
17391 cls : 'control-label',
17392 html : this.fieldLabel
17403 if(this.indicatorpos == 'right'){
17407 cls : 'control-label',
17408 html : this.fieldLabel,
17412 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17413 tooltip : 'This field is required'
17430 var settings = this;
17432 ['xs','sm','md','lg'].map(function(size){
17433 if (settings[size]) {
17434 cfg.cls += ' col-' + size + '-' + settings[size];
17441 initTouchView : function()
17443 this.renderTouchView();
17445 this.touchViewEl.on('scroll', function(){
17446 this.el.dom.scrollTop = 0;
17449 this.originalValue = this.getValue();
17451 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17453 this.inputEl().on("click", this.showTouchView, this);
17454 if (this.triggerEl) {
17455 this.triggerEl.on("click", this.showTouchView, this);
17459 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17460 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17462 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17464 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17465 this.store.on('load', this.onTouchViewLoad, this);
17466 this.store.on('loadexception', this.onTouchViewLoadException, this);
17468 if(this.hiddenName){
17470 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17472 this.hiddenField.dom.value =
17473 this.hiddenValue !== undefined ? this.hiddenValue :
17474 this.value !== undefined ? this.value : '';
17476 this.el.dom.removeAttribute('name');
17477 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17481 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17482 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17485 if(this.removable && !this.multiple){
17486 var close = this.closeTriggerEl();
17488 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17489 close.on('click', this.removeBtnClick, this, close);
17493 * fix the bug in Safari iOS8
17495 this.inputEl().on("focus", function(e){
17496 document.activeElement.blur();
17499 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17506 renderTouchView : function()
17508 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17509 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17511 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17512 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17514 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17515 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17516 this.touchViewBodyEl.setStyle('overflow', 'auto');
17518 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17519 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17521 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17522 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17526 showTouchView : function()
17532 this.touchViewHeaderEl.hide();
17534 if(this.modalTitle.length){
17535 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17536 this.touchViewHeaderEl.show();
17539 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17540 this.touchViewEl.show();
17542 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17544 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17545 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17547 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17549 if(this.modalTitle.length){
17550 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17553 this.touchViewBodyEl.setHeight(bodyHeight);
17557 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17559 this.touchViewEl.addClass(['in','show']);
17562 if(this._touchViewMask){
17563 Roo.get(document.body).addClass("x-body-masked");
17564 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17565 this._touchViewMask.setStyle('z-index', 10000);
17566 this._touchViewMask.addClass('show');
17569 this.doTouchViewQuery();
17573 hideTouchView : function()
17575 this.touchViewEl.removeClass(['in','show']);
17579 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17581 this.touchViewEl.setStyle('display', 'none');
17584 if(this._touchViewMask){
17585 this._touchViewMask.removeClass('show');
17586 Roo.get(document.body).removeClass("x-body-masked");
17590 setTouchViewValue : function()
17597 Roo.each(this.tickItems, function(o){
17602 this.hideTouchView();
17605 doTouchViewQuery : function()
17614 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17618 if(!this.alwaysQuery || this.mode == 'local'){
17619 this.onTouchViewLoad();
17626 onTouchViewBeforeLoad : function(combo,opts)
17632 onTouchViewLoad : function()
17634 if(this.store.getCount() < 1){
17635 this.onTouchViewEmptyResults();
17639 this.clearTouchView();
17641 var rawValue = this.getRawValue();
17643 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17645 this.tickItems = [];
17647 this.store.data.each(function(d, rowIndex){
17648 var row = this.touchViewListGroup.createChild(template);
17650 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17651 row.addClass(d.data.cls);
17654 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17657 html : d.data[this.displayField]
17660 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17661 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17664 row.removeClass('selected');
17665 if(!this.multiple && this.valueField &&
17666 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17669 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17670 row.addClass('selected');
17673 if(this.multiple && this.valueField &&
17674 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17678 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17679 this.tickItems.push(d.data);
17682 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17686 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17688 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17690 if(this.modalTitle.length){
17691 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17694 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17696 if(this.mobile_restrict_height && listHeight < bodyHeight){
17697 this.touchViewBodyEl.setHeight(listHeight);
17702 if(firstChecked && listHeight > bodyHeight){
17703 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17708 onTouchViewLoadException : function()
17710 this.hideTouchView();
17713 onTouchViewEmptyResults : function()
17715 this.clearTouchView();
17717 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17719 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17723 clearTouchView : function()
17725 this.touchViewListGroup.dom.innerHTML = '';
17728 onTouchViewClick : function(e, el, o)
17730 e.preventDefault();
17733 var rowIndex = o.rowIndex;
17735 var r = this.store.getAt(rowIndex);
17737 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17739 if(!this.multiple){
17740 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17741 c.dom.removeAttribute('checked');
17744 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17746 this.setFromData(r.data);
17748 var close = this.closeTriggerEl();
17754 this.hideTouchView();
17756 this.fireEvent('select', this, r, rowIndex);
17761 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17762 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17763 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17767 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17768 this.addItem(r.data);
17769 this.tickItems.push(r.data);
17773 getAutoCreateNativeIOS : function()
17776 cls: 'form-group' //input-group,
17781 cls : 'roo-ios-select'
17785 combobox.name = this.name;
17788 if (this.disabled) {
17789 combobox.disabled = true;
17792 var settings = this;
17794 ['xs','sm','md','lg'].map(function(size){
17795 if (settings[size]) {
17796 cfg.cls += ' col-' + size + '-' + settings[size];
17806 initIOSView : function()
17808 this.store.on('load', this.onIOSViewLoad, this);
17813 onIOSViewLoad : function()
17815 if(this.store.getCount() < 1){
17819 this.clearIOSView();
17821 if(this.allowBlank) {
17823 var default_text = '-- SELECT --';
17825 if(this.placeholder.length){
17826 default_text = this.placeholder;
17829 if(this.emptyTitle.length){
17830 default_text += ' - ' + this.emptyTitle + ' -';
17833 var opt = this.inputEl().createChild({
17836 html : default_text
17840 o[this.valueField] = 0;
17841 o[this.displayField] = default_text;
17843 this.ios_options.push({
17850 this.store.data.each(function(d, rowIndex){
17854 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17855 html = d.data[this.displayField];
17860 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17861 value = d.data[this.valueField];
17870 if(this.value == d.data[this.valueField]){
17871 option['selected'] = true;
17874 var opt = this.inputEl().createChild(option);
17876 this.ios_options.push({
17883 this.inputEl().on('change', function(){
17884 this.fireEvent('select', this);
17889 clearIOSView: function()
17891 this.inputEl().dom.innerHTML = '';
17893 this.ios_options = [];
17896 setIOSValue: function(v)
17900 if(!this.ios_options){
17904 Roo.each(this.ios_options, function(opts){
17906 opts.el.dom.removeAttribute('selected');
17908 if(opts.data[this.valueField] != v){
17912 opts.el.dom.setAttribute('selected', true);
17918 * @cfg {Boolean} grow
17922 * @cfg {Number} growMin
17926 * @cfg {Number} growMax
17935 Roo.apply(Roo.bootstrap.ComboBox, {
17939 cls: 'modal-header',
17961 cls: 'list-group-item',
17965 cls: 'roo-combobox-list-group-item-value'
17969 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17983 listItemCheckbox : {
17985 cls: 'list-group-item',
17989 cls: 'roo-combobox-list-group-item-value'
17993 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18009 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18014 cls: 'modal-footer',
18022 cls: 'col-xs-6 text-left',
18025 cls: 'btn btn-danger roo-touch-view-cancel',
18031 cls: 'col-xs-6 text-right',
18034 cls: 'btn btn-success roo-touch-view-ok',
18045 Roo.apply(Roo.bootstrap.ComboBox, {
18047 touchViewTemplate : {
18049 cls: 'modal fade roo-combobox-touch-view',
18053 cls: 'modal-dialog',
18054 style : 'position:fixed', // we have to fix position....
18058 cls: 'modal-content',
18060 Roo.bootstrap.ComboBox.header,
18061 Roo.bootstrap.ComboBox.body,
18062 Roo.bootstrap.ComboBox.footer
18071 * Ext JS Library 1.1.1
18072 * Copyright(c) 2006-2007, Ext JS, LLC.
18074 * Originally Released Under LGPL - original licence link has changed is not relivant.
18077 * <script type="text/javascript">
18082 * @extends Roo.util.Observable
18083 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18084 * This class also supports single and multi selection modes. <br>
18085 * Create a data model bound view:
18087 var store = new Roo.data.Store(...);
18089 var view = new Roo.View({
18091 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18093 singleSelect: true,
18094 selectedClass: "ydataview-selected",
18098 // listen for node click?
18099 view.on("click", function(vw, index, node, e){
18100 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18104 dataModel.load("foobar.xml");
18106 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18108 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18109 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18111 * Note: old style constructor is still suported (container, template, config)
18114 * Create a new View
18115 * @param {Object} config The config object
18118 Roo.View = function(config, depreciated_tpl, depreciated_config){
18120 this.parent = false;
18122 if (typeof(depreciated_tpl) == 'undefined') {
18123 // new way.. - universal constructor.
18124 Roo.apply(this, config);
18125 this.el = Roo.get(this.el);
18128 this.el = Roo.get(config);
18129 this.tpl = depreciated_tpl;
18130 Roo.apply(this, depreciated_config);
18132 this.wrapEl = this.el.wrap().wrap();
18133 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18136 if(typeof(this.tpl) == "string"){
18137 this.tpl = new Roo.Template(this.tpl);
18139 // support xtype ctors..
18140 this.tpl = new Roo.factory(this.tpl, Roo);
18144 this.tpl.compile();
18149 * @event beforeclick
18150 * Fires before a click is processed. Returns false to cancel the default action.
18151 * @param {Roo.View} this
18152 * @param {Number} index The index of the target node
18153 * @param {HTMLElement} node The target node
18154 * @param {Roo.EventObject} e The raw event object
18156 "beforeclick" : true,
18159 * Fires when a template node is clicked.
18160 * @param {Roo.View} this
18161 * @param {Number} index The index of the target node
18162 * @param {HTMLElement} node The target node
18163 * @param {Roo.EventObject} e The raw event object
18168 * Fires when a template node is double clicked.
18169 * @param {Roo.View} this
18170 * @param {Number} index The index of the target node
18171 * @param {HTMLElement} node The target node
18172 * @param {Roo.EventObject} e The raw event object
18176 * @event contextmenu
18177 * Fires when a template node is right clicked.
18178 * @param {Roo.View} this
18179 * @param {Number} index The index of the target node
18180 * @param {HTMLElement} node The target node
18181 * @param {Roo.EventObject} e The raw event object
18183 "contextmenu" : true,
18185 * @event selectionchange
18186 * Fires when the selected nodes change.
18187 * @param {Roo.View} this
18188 * @param {Array} selections Array of the selected nodes
18190 "selectionchange" : true,
18193 * @event beforeselect
18194 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18195 * @param {Roo.View} this
18196 * @param {HTMLElement} node The node to be selected
18197 * @param {Array} selections Array of currently selected nodes
18199 "beforeselect" : true,
18201 * @event preparedata
18202 * Fires on every row to render, to allow you to change the data.
18203 * @param {Roo.View} this
18204 * @param {Object} data to be rendered (change this)
18206 "preparedata" : true
18214 "click": this.onClick,
18215 "dblclick": this.onDblClick,
18216 "contextmenu": this.onContextMenu,
18220 this.selections = [];
18222 this.cmp = new Roo.CompositeElementLite([]);
18224 this.store = Roo.factory(this.store, Roo.data);
18225 this.setStore(this.store, true);
18228 if ( this.footer && this.footer.xtype) {
18230 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18232 this.footer.dataSource = this.store;
18233 this.footer.container = fctr;
18234 this.footer = Roo.factory(this.footer, Roo);
18235 fctr.insertFirst(this.el);
18237 // this is a bit insane - as the paging toolbar seems to detach the el..
18238 // dom.parentNode.parentNode.parentNode
18239 // they get detached?
18243 Roo.View.superclass.constructor.call(this);
18248 Roo.extend(Roo.View, Roo.util.Observable, {
18251 * @cfg {Roo.data.Store} store Data store to load data from.
18256 * @cfg {String|Roo.Element} el The container element.
18261 * @cfg {String|Roo.Template} tpl The template used by this View
18265 * @cfg {String} dataName the named area of the template to use as the data area
18266 * Works with domtemplates roo-name="name"
18270 * @cfg {String} selectedClass The css class to add to selected nodes
18272 selectedClass : "x-view-selected",
18274 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18279 * @cfg {String} text to display on mask (default Loading)
18283 * @cfg {Boolean} multiSelect Allow multiple selection
18285 multiSelect : false,
18287 * @cfg {Boolean} singleSelect Allow single selection
18289 singleSelect: false,
18292 * @cfg {Boolean} toggleSelect - selecting
18294 toggleSelect : false,
18297 * @cfg {Boolean} tickable - selecting
18302 * Returns the element this view is bound to.
18303 * @return {Roo.Element}
18305 getEl : function(){
18306 return this.wrapEl;
18312 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18314 refresh : function(){
18315 //Roo.log('refresh');
18318 // if we are using something like 'domtemplate', then
18319 // the what gets used is:
18320 // t.applySubtemplate(NAME, data, wrapping data..)
18321 // the outer template then get' applied with
18322 // the store 'extra data'
18323 // and the body get's added to the
18324 // roo-name="data" node?
18325 // <span class='roo-tpl-{name}'></span> ?????
18329 this.clearSelections();
18330 this.el.update("");
18332 var records = this.store.getRange();
18333 if(records.length < 1) {
18335 // is this valid?? = should it render a template??
18337 this.el.update(this.emptyText);
18341 if (this.dataName) {
18342 this.el.update(t.apply(this.store.meta)); //????
18343 el = this.el.child('.roo-tpl-' + this.dataName);
18346 for(var i = 0, len = records.length; i < len; i++){
18347 var data = this.prepareData(records[i].data, i, records[i]);
18348 this.fireEvent("preparedata", this, data, i, records[i]);
18350 var d = Roo.apply({}, data);
18353 Roo.apply(d, {'roo-id' : Roo.id()});
18357 Roo.each(this.parent.item, function(item){
18358 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18361 Roo.apply(d, {'roo-data-checked' : 'checked'});
18365 html[html.length] = Roo.util.Format.trim(
18367 t.applySubtemplate(this.dataName, d, this.store.meta) :
18374 el.update(html.join(""));
18375 this.nodes = el.dom.childNodes;
18376 this.updateIndexes(0);
18381 * Function to override to reformat the data that is sent to
18382 * the template for each node.
18383 * DEPRICATED - use the preparedata event handler.
18384 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18385 * a JSON object for an UpdateManager bound view).
18387 prepareData : function(data, index, record)
18389 this.fireEvent("preparedata", this, data, index, record);
18393 onUpdate : function(ds, record){
18394 // Roo.log('on update');
18395 this.clearSelections();
18396 var index = this.store.indexOf(record);
18397 var n = this.nodes[index];
18398 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18399 n.parentNode.removeChild(n);
18400 this.updateIndexes(index, index);
18406 onAdd : function(ds, records, index)
18408 //Roo.log(['on Add', ds, records, index] );
18409 this.clearSelections();
18410 if(this.nodes.length == 0){
18414 var n = this.nodes[index];
18415 for(var i = 0, len = records.length; i < len; i++){
18416 var d = this.prepareData(records[i].data, i, records[i]);
18418 this.tpl.insertBefore(n, d);
18421 this.tpl.append(this.el, d);
18424 this.updateIndexes(index);
18427 onRemove : function(ds, record, index){
18428 // Roo.log('onRemove');
18429 this.clearSelections();
18430 var el = this.dataName ?
18431 this.el.child('.roo-tpl-' + this.dataName) :
18434 el.dom.removeChild(this.nodes[index]);
18435 this.updateIndexes(index);
18439 * Refresh an individual node.
18440 * @param {Number} index
18442 refreshNode : function(index){
18443 this.onUpdate(this.store, this.store.getAt(index));
18446 updateIndexes : function(startIndex, endIndex){
18447 var ns = this.nodes;
18448 startIndex = startIndex || 0;
18449 endIndex = endIndex || ns.length - 1;
18450 for(var i = startIndex; i <= endIndex; i++){
18451 ns[i].nodeIndex = i;
18456 * Changes the data store this view uses and refresh the view.
18457 * @param {Store} store
18459 setStore : function(store, initial){
18460 if(!initial && this.store){
18461 this.store.un("datachanged", this.refresh);
18462 this.store.un("add", this.onAdd);
18463 this.store.un("remove", this.onRemove);
18464 this.store.un("update", this.onUpdate);
18465 this.store.un("clear", this.refresh);
18466 this.store.un("beforeload", this.onBeforeLoad);
18467 this.store.un("load", this.onLoad);
18468 this.store.un("loadexception", this.onLoad);
18472 store.on("datachanged", this.refresh, this);
18473 store.on("add", this.onAdd, this);
18474 store.on("remove", this.onRemove, this);
18475 store.on("update", this.onUpdate, this);
18476 store.on("clear", this.refresh, this);
18477 store.on("beforeload", this.onBeforeLoad, this);
18478 store.on("load", this.onLoad, this);
18479 store.on("loadexception", this.onLoad, this);
18487 * onbeforeLoad - masks the loading area.
18490 onBeforeLoad : function(store,opts)
18492 //Roo.log('onBeforeLoad');
18494 this.el.update("");
18496 this.el.mask(this.mask ? this.mask : "Loading" );
18498 onLoad : function ()
18505 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18506 * @param {HTMLElement} node
18507 * @return {HTMLElement} The template node
18509 findItemFromChild : function(node){
18510 var el = this.dataName ?
18511 this.el.child('.roo-tpl-' + this.dataName,true) :
18514 if(!node || node.parentNode == el){
18517 var p = node.parentNode;
18518 while(p && p != el){
18519 if(p.parentNode == el){
18528 onClick : function(e){
18529 var item = this.findItemFromChild(e.getTarget());
18531 var index = this.indexOf(item);
18532 if(this.onItemClick(item, index, e) !== false){
18533 this.fireEvent("click", this, index, item, e);
18536 this.clearSelections();
18541 onContextMenu : function(e){
18542 var item = this.findItemFromChild(e.getTarget());
18544 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18549 onDblClick : function(e){
18550 var item = this.findItemFromChild(e.getTarget());
18552 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18556 onItemClick : function(item, index, e)
18558 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18561 if (this.toggleSelect) {
18562 var m = this.isSelected(item) ? 'unselect' : 'select';
18565 _t[m](item, true, false);
18568 if(this.multiSelect || this.singleSelect){
18569 if(this.multiSelect && e.shiftKey && this.lastSelection){
18570 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18572 this.select(item, this.multiSelect && e.ctrlKey);
18573 this.lastSelection = item;
18576 if(!this.tickable){
18577 e.preventDefault();
18585 * Get the number of selected nodes.
18588 getSelectionCount : function(){
18589 return this.selections.length;
18593 * Get the currently selected nodes.
18594 * @return {Array} An array of HTMLElements
18596 getSelectedNodes : function(){
18597 return this.selections;
18601 * Get the indexes of the selected nodes.
18604 getSelectedIndexes : function(){
18605 var indexes = [], s = this.selections;
18606 for(var i = 0, len = s.length; i < len; i++){
18607 indexes.push(s[i].nodeIndex);
18613 * Clear all selections
18614 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18616 clearSelections : function(suppressEvent){
18617 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18618 this.cmp.elements = this.selections;
18619 this.cmp.removeClass(this.selectedClass);
18620 this.selections = [];
18621 if(!suppressEvent){
18622 this.fireEvent("selectionchange", this, this.selections);
18628 * Returns true if the passed node is selected
18629 * @param {HTMLElement/Number} node The node or node index
18630 * @return {Boolean}
18632 isSelected : function(node){
18633 var s = this.selections;
18637 node = this.getNode(node);
18638 return s.indexOf(node) !== -1;
18643 * @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
18644 * @param {Boolean} keepExisting (optional) true to keep existing selections
18645 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18647 select : function(nodeInfo, keepExisting, suppressEvent){
18648 if(nodeInfo instanceof Array){
18650 this.clearSelections(true);
18652 for(var i = 0, len = nodeInfo.length; i < len; i++){
18653 this.select(nodeInfo[i], true, true);
18657 var node = this.getNode(nodeInfo);
18658 if(!node || this.isSelected(node)){
18659 return; // already selected.
18662 this.clearSelections(true);
18665 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18666 Roo.fly(node).addClass(this.selectedClass);
18667 this.selections.push(node);
18668 if(!suppressEvent){
18669 this.fireEvent("selectionchange", this, this.selections);
18677 * @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
18678 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18679 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18681 unselect : function(nodeInfo, keepExisting, suppressEvent)
18683 if(nodeInfo instanceof Array){
18684 Roo.each(this.selections, function(s) {
18685 this.unselect(s, nodeInfo);
18689 var node = this.getNode(nodeInfo);
18690 if(!node || !this.isSelected(node)){
18691 //Roo.log("not selected");
18692 return; // not selected.
18696 Roo.each(this.selections, function(s) {
18698 Roo.fly(node).removeClass(this.selectedClass);
18705 this.selections= ns;
18706 this.fireEvent("selectionchange", this, this.selections);
18710 * Gets a template node.
18711 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18712 * @return {HTMLElement} The node or null if it wasn't found
18714 getNode : function(nodeInfo){
18715 if(typeof nodeInfo == "string"){
18716 return document.getElementById(nodeInfo);
18717 }else if(typeof nodeInfo == "number"){
18718 return this.nodes[nodeInfo];
18724 * Gets a range template nodes.
18725 * @param {Number} startIndex
18726 * @param {Number} endIndex
18727 * @return {Array} An array of nodes
18729 getNodes : function(start, end){
18730 var ns = this.nodes;
18731 start = start || 0;
18732 end = typeof end == "undefined" ? ns.length - 1 : end;
18735 for(var i = start; i <= end; i++){
18739 for(var i = start; i >= end; i--){
18747 * Finds the index of the passed node
18748 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18749 * @return {Number} The index of the node or -1
18751 indexOf : function(node){
18752 node = this.getNode(node);
18753 if(typeof node.nodeIndex == "number"){
18754 return node.nodeIndex;
18756 var ns = this.nodes;
18757 for(var i = 0, len = ns.length; i < len; i++){
18768 * based on jquery fullcalendar
18772 Roo.bootstrap = Roo.bootstrap || {};
18774 * @class Roo.bootstrap.Calendar
18775 * @extends Roo.bootstrap.Component
18776 * Bootstrap Calendar class
18777 * @cfg {Boolean} loadMask (true|false) default false
18778 * @cfg {Object} header generate the user specific header of the calendar, default false
18781 * Create a new Container
18782 * @param {Object} config The config object
18787 Roo.bootstrap.Calendar = function(config){
18788 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18792 * Fires when a date is selected
18793 * @param {DatePicker} this
18794 * @param {Date} date The selected date
18798 * @event monthchange
18799 * Fires when the displayed month changes
18800 * @param {DatePicker} this
18801 * @param {Date} date The selected month
18803 'monthchange': true,
18805 * @event evententer
18806 * Fires when mouse over an event
18807 * @param {Calendar} this
18808 * @param {event} Event
18810 'evententer': true,
18812 * @event eventleave
18813 * Fires when the mouse leaves an
18814 * @param {Calendar} this
18817 'eventleave': true,
18819 * @event eventclick
18820 * Fires when the mouse click an
18821 * @param {Calendar} this
18830 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18833 * @cfg {Number} startDay
18834 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18842 getAutoCreate : function(){
18845 var fc_button = function(name, corner, style, content ) {
18846 return Roo.apply({},{
18848 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18850 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18853 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18864 style : 'width:100%',
18871 cls : 'fc-header-left',
18873 fc_button('prev', 'left', 'arrow', '‹' ),
18874 fc_button('next', 'right', 'arrow', '›' ),
18875 { tag: 'span', cls: 'fc-header-space' },
18876 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18884 cls : 'fc-header-center',
18888 cls: 'fc-header-title',
18891 html : 'month / year'
18899 cls : 'fc-header-right',
18901 /* fc_button('month', 'left', '', 'month' ),
18902 fc_button('week', '', '', 'week' ),
18903 fc_button('day', 'right', '', 'day' )
18915 header = this.header;
18918 var cal_heads = function() {
18920 // fixme - handle this.
18922 for (var i =0; i < Date.dayNames.length; i++) {
18923 var d = Date.dayNames[i];
18926 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18927 html : d.substring(0,3)
18931 ret[0].cls += ' fc-first';
18932 ret[6].cls += ' fc-last';
18935 var cal_cell = function(n) {
18938 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18943 cls: 'fc-day-number',
18947 cls: 'fc-day-content',
18951 style: 'position: relative;' // height: 17px;
18963 var cal_rows = function() {
18966 for (var r = 0; r < 6; r++) {
18973 for (var i =0; i < Date.dayNames.length; i++) {
18974 var d = Date.dayNames[i];
18975 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18978 row.cn[0].cls+=' fc-first';
18979 row.cn[0].cn[0].style = 'min-height:90px';
18980 row.cn[6].cls+=' fc-last';
18984 ret[0].cls += ' fc-first';
18985 ret[4].cls += ' fc-prev-last';
18986 ret[5].cls += ' fc-last';
18993 cls: 'fc-border-separate',
18994 style : 'width:100%',
19002 cls : 'fc-first fc-last',
19020 cls : 'fc-content',
19021 style : "position: relative;",
19024 cls : 'fc-view fc-view-month fc-grid',
19025 style : 'position: relative',
19026 unselectable : 'on',
19029 cls : 'fc-event-container',
19030 style : 'position:absolute;z-index:8;top:0;left:0;'
19048 initEvents : function()
19051 throw "can not find store for calendar";
19057 style: "text-align:center",
19061 style: "background-color:white;width:50%;margin:250 auto",
19065 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19076 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19078 var size = this.el.select('.fc-content', true).first().getSize();
19079 this.maskEl.setSize(size.width, size.height);
19080 this.maskEl.enableDisplayMode("block");
19081 if(!this.loadMask){
19082 this.maskEl.hide();
19085 this.store = Roo.factory(this.store, Roo.data);
19086 this.store.on('load', this.onLoad, this);
19087 this.store.on('beforeload', this.onBeforeLoad, this);
19091 this.cells = this.el.select('.fc-day',true);
19092 //Roo.log(this.cells);
19093 this.textNodes = this.el.query('.fc-day-number');
19094 this.cells.addClassOnOver('fc-state-hover');
19096 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19097 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19098 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19099 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19101 this.on('monthchange', this.onMonthChange, this);
19103 this.update(new Date().clearTime());
19106 resize : function() {
19107 var sz = this.el.getSize();
19109 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19110 this.el.select('.fc-day-content div',true).setHeight(34);
19115 showPrevMonth : function(e){
19116 this.update(this.activeDate.add("mo", -1));
19118 showToday : function(e){
19119 this.update(new Date().clearTime());
19122 showNextMonth : function(e){
19123 this.update(this.activeDate.add("mo", 1));
19127 showPrevYear : function(){
19128 this.update(this.activeDate.add("y", -1));
19132 showNextYear : function(){
19133 this.update(this.activeDate.add("y", 1));
19138 update : function(date)
19140 var vd = this.activeDate;
19141 this.activeDate = date;
19142 // if(vd && this.el){
19143 // var t = date.getTime();
19144 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19145 // Roo.log('using add remove');
19147 // this.fireEvent('monthchange', this, date);
19149 // this.cells.removeClass("fc-state-highlight");
19150 // this.cells.each(function(c){
19151 // if(c.dateValue == t){
19152 // c.addClass("fc-state-highlight");
19153 // setTimeout(function(){
19154 // try{c.dom.firstChild.focus();}catch(e){}
19164 var days = date.getDaysInMonth();
19166 var firstOfMonth = date.getFirstDateOfMonth();
19167 var startingPos = firstOfMonth.getDay()-this.startDay;
19169 if(startingPos < this.startDay){
19173 var pm = date.add(Date.MONTH, -1);
19174 var prevStart = pm.getDaysInMonth()-startingPos;
19176 this.cells = this.el.select('.fc-day',true);
19177 this.textNodes = this.el.query('.fc-day-number');
19178 this.cells.addClassOnOver('fc-state-hover');
19180 var cells = this.cells.elements;
19181 var textEls = this.textNodes;
19183 Roo.each(cells, function(cell){
19184 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19187 days += startingPos;
19189 // convert everything to numbers so it's fast
19190 var day = 86400000;
19191 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19194 //Roo.log(prevStart);
19196 var today = new Date().clearTime().getTime();
19197 var sel = date.clearTime().getTime();
19198 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19199 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19200 var ddMatch = this.disabledDatesRE;
19201 var ddText = this.disabledDatesText;
19202 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19203 var ddaysText = this.disabledDaysText;
19204 var format = this.format;
19206 var setCellClass = function(cal, cell){
19210 //Roo.log('set Cell Class');
19212 var t = d.getTime();
19216 cell.dateValue = t;
19218 cell.className += " fc-today";
19219 cell.className += " fc-state-highlight";
19220 cell.title = cal.todayText;
19223 // disable highlight in other month..
19224 //cell.className += " fc-state-highlight";
19229 cell.className = " fc-state-disabled";
19230 cell.title = cal.minText;
19234 cell.className = " fc-state-disabled";
19235 cell.title = cal.maxText;
19239 if(ddays.indexOf(d.getDay()) != -1){
19240 cell.title = ddaysText;
19241 cell.className = " fc-state-disabled";
19244 if(ddMatch && format){
19245 var fvalue = d.dateFormat(format);
19246 if(ddMatch.test(fvalue)){
19247 cell.title = ddText.replace("%0", fvalue);
19248 cell.className = " fc-state-disabled";
19252 if (!cell.initialClassName) {
19253 cell.initialClassName = cell.dom.className;
19256 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19261 for(; i < startingPos; i++) {
19262 textEls[i].innerHTML = (++prevStart);
19263 d.setDate(d.getDate()+1);
19265 cells[i].className = "fc-past fc-other-month";
19266 setCellClass(this, cells[i]);
19271 for(; i < days; i++){
19272 intDay = i - startingPos + 1;
19273 textEls[i].innerHTML = (intDay);
19274 d.setDate(d.getDate()+1);
19276 cells[i].className = ''; // "x-date-active";
19277 setCellClass(this, cells[i]);
19281 for(; i < 42; i++) {
19282 textEls[i].innerHTML = (++extraDays);
19283 d.setDate(d.getDate()+1);
19285 cells[i].className = "fc-future fc-other-month";
19286 setCellClass(this, cells[i]);
19289 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19291 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19293 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19294 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19296 if(totalRows != 6){
19297 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19298 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19301 this.fireEvent('monthchange', this, date);
19305 if(!this.internalRender){
19306 var main = this.el.dom.firstChild;
19307 var w = main.offsetWidth;
19308 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19309 Roo.fly(main).setWidth(w);
19310 this.internalRender = true;
19311 // opera does not respect the auto grow header center column
19312 // then, after it gets a width opera refuses to recalculate
19313 // without a second pass
19314 if(Roo.isOpera && !this.secondPass){
19315 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19316 this.secondPass = true;
19317 this.update.defer(10, this, [date]);
19324 findCell : function(dt) {
19325 dt = dt.clearTime().getTime();
19327 this.cells.each(function(c){
19328 //Roo.log("check " +c.dateValue + '?=' + dt);
19329 if(c.dateValue == dt){
19339 findCells : function(ev) {
19340 var s = ev.start.clone().clearTime().getTime();
19342 var e= ev.end.clone().clearTime().getTime();
19345 this.cells.each(function(c){
19346 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19348 if(c.dateValue > e){
19351 if(c.dateValue < s){
19360 // findBestRow: function(cells)
19364 // for (var i =0 ; i < cells.length;i++) {
19365 // ret = Math.max(cells[i].rows || 0,ret);
19372 addItem : function(ev)
19374 // look for vertical location slot in
19375 var cells = this.findCells(ev);
19377 // ev.row = this.findBestRow(cells);
19379 // work out the location.
19383 for(var i =0; i < cells.length; i++) {
19385 cells[i].row = cells[0].row;
19388 cells[i].row = cells[i].row + 1;
19398 if (crow.start.getY() == cells[i].getY()) {
19400 crow.end = cells[i];
19417 cells[0].events.push(ev);
19419 this.calevents.push(ev);
19422 clearEvents: function() {
19424 if(!this.calevents){
19428 Roo.each(this.cells.elements, function(c){
19434 Roo.each(this.calevents, function(e) {
19435 Roo.each(e.els, function(el) {
19436 el.un('mouseenter' ,this.onEventEnter, this);
19437 el.un('mouseleave' ,this.onEventLeave, this);
19442 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19448 renderEvents: function()
19452 this.cells.each(function(c) {
19461 if(c.row != c.events.length){
19462 r = 4 - (4 - (c.row - c.events.length));
19465 c.events = ev.slice(0, r);
19466 c.more = ev.slice(r);
19468 if(c.more.length && c.more.length == 1){
19469 c.events.push(c.more.pop());
19472 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19476 this.cells.each(function(c) {
19478 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19481 for (var e = 0; e < c.events.length; e++){
19482 var ev = c.events[e];
19483 var rows = ev.rows;
19485 for(var i = 0; i < rows.length; i++) {
19487 // how many rows should it span..
19490 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19491 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19493 unselectable : "on",
19496 cls: 'fc-event-inner',
19500 // cls: 'fc-event-time',
19501 // html : cells.length > 1 ? '' : ev.time
19505 cls: 'fc-event-title',
19506 html : String.format('{0}', ev.title)
19513 cls: 'ui-resizable-handle ui-resizable-e',
19514 html : '  '
19521 cfg.cls += ' fc-event-start';
19523 if ((i+1) == rows.length) {
19524 cfg.cls += ' fc-event-end';
19527 var ctr = _this.el.select('.fc-event-container',true).first();
19528 var cg = ctr.createChild(cfg);
19530 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19531 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19533 var r = (c.more.length) ? 1 : 0;
19534 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19535 cg.setWidth(ebox.right - sbox.x -2);
19537 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19538 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19539 cg.on('click', _this.onEventClick, _this, ev);
19550 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19551 style : 'position: absolute',
19552 unselectable : "on",
19555 cls: 'fc-event-inner',
19559 cls: 'fc-event-title',
19567 cls: 'ui-resizable-handle ui-resizable-e',
19568 html : '  '
19574 var ctr = _this.el.select('.fc-event-container',true).first();
19575 var cg = ctr.createChild(cfg);
19577 var sbox = c.select('.fc-day-content',true).first().getBox();
19578 var ebox = c.select('.fc-day-content',true).first().getBox();
19580 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19581 cg.setWidth(ebox.right - sbox.x -2);
19583 cg.on('click', _this.onMoreEventClick, _this, c.more);
19593 onEventEnter: function (e, el,event,d) {
19594 this.fireEvent('evententer', this, el, event);
19597 onEventLeave: function (e, el,event,d) {
19598 this.fireEvent('eventleave', this, el, event);
19601 onEventClick: function (e, el,event,d) {
19602 this.fireEvent('eventclick', this, el, event);
19605 onMonthChange: function () {
19609 onMoreEventClick: function(e, el, more)
19613 this.calpopover.placement = 'right';
19614 this.calpopover.setTitle('More');
19616 this.calpopover.setContent('');
19618 var ctr = this.calpopover.el.select('.popover-content', true).first();
19620 Roo.each(more, function(m){
19622 cls : 'fc-event-hori fc-event-draggable',
19625 var cg = ctr.createChild(cfg);
19627 cg.on('click', _this.onEventClick, _this, m);
19630 this.calpopover.show(el);
19635 onLoad: function ()
19637 this.calevents = [];
19640 if(this.store.getCount() > 0){
19641 this.store.data.each(function(d){
19644 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19645 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19646 time : d.data.start_time,
19647 title : d.data.title,
19648 description : d.data.description,
19649 venue : d.data.venue
19654 this.renderEvents();
19656 if(this.calevents.length && this.loadMask){
19657 this.maskEl.hide();
19661 onBeforeLoad: function()
19663 this.clearEvents();
19665 this.maskEl.show();
19679 * @class Roo.bootstrap.Popover
19680 * @extends Roo.bootstrap.Component
19681 * Bootstrap Popover class
19682 * @cfg {String} html contents of the popover (or false to use children..)
19683 * @cfg {String} title of popover (or false to hide)
19684 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19685 * @cfg {String} trigger click || hover (or false to trigger manually)
19686 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19687 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19688 * - if false and it has a 'parent' then it will be automatically added to that element
19689 * - if string - Roo.get will be called
19690 * @cfg {Number} delay - delay before showing
19693 * Create a new Popover
19694 * @param {Object} config The config object
19697 Roo.bootstrap.Popover = function(config){
19698 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19704 * After the popover show
19706 * @param {Roo.bootstrap.Popover} this
19711 * After the popover hide
19713 * @param {Roo.bootstrap.Popover} this
19719 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19724 placement : 'right',
19725 trigger : 'hover', // hover
19731 can_build_overlaid : false,
19733 maskEl : false, // the mask element
19736 alignEl : false, // when show is called with an element - this get's stored.
19738 getChildContainer : function()
19740 return this.contentEl;
19743 getPopoverHeader : function()
19745 this.title = true; // flag not to hide it..
19746 this.headerEl.addClass('p-0');
19747 return this.headerEl
19751 getAutoCreate : function(){
19754 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19755 style: 'display:block',
19761 cls : 'popover-inner ',
19765 cls: 'popover-title popover-header',
19766 html : this.title === false ? '' : this.title
19769 cls : 'popover-content popover-body ' + (this.cls || ''),
19770 html : this.html || ''
19781 * @param {string} the title
19783 setTitle: function(str)
19787 this.headerEl.dom.innerHTML = str;
19792 * @param {string} the body content
19794 setContent: function(str)
19797 if (this.contentEl) {
19798 this.contentEl.dom.innerHTML = str;
19802 // as it get's added to the bottom of the page.
19803 onRender : function(ct, position)
19805 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19810 var cfg = Roo.apply({}, this.getAutoCreate());
19814 cfg.cls += ' ' + this.cls;
19817 cfg.style = this.style;
19819 //Roo.log("adding to ");
19820 this.el = Roo.get(document.body).createChild(cfg, position);
19821 // Roo.log(this.el);
19824 this.contentEl = this.el.select('.popover-content',true).first();
19825 this.headerEl = this.el.select('.popover-title',true).first();
19828 if(typeof(this.items) != 'undefined'){
19829 var items = this.items;
19832 for(var i =0;i < items.length;i++) {
19833 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19837 this.items = nitems;
19839 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19840 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19847 resizeMask : function()
19849 this.maskEl.setSize(
19850 Roo.lib.Dom.getViewWidth(true),
19851 Roo.lib.Dom.getViewHeight(true)
19855 initEvents : function()
19859 Roo.bootstrap.Popover.register(this);
19862 this.arrowEl = this.el.select('.arrow',true).first();
19863 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19864 this.el.enableDisplayMode('block');
19868 if (this.over === false && !this.parent()) {
19871 if (this.triggers === false) {
19876 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19877 var triggers = this.trigger ? this.trigger.split(' ') : [];
19878 Roo.each(triggers, function(trigger) {
19880 if (trigger == 'click') {
19881 on_el.on('click', this.toggle, this);
19882 } else if (trigger != 'manual') {
19883 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19884 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19886 on_el.on(eventIn ,this.enter, this);
19887 on_el.on(eventOut, this.leave, this);
19897 toggle : function () {
19898 this.hoverState == 'in' ? this.leave() : this.enter();
19901 enter : function () {
19903 clearTimeout(this.timeout);
19905 this.hoverState = 'in';
19907 if (!this.delay || !this.delay.show) {
19912 this.timeout = setTimeout(function () {
19913 if (_t.hoverState == 'in') {
19916 }, this.delay.show)
19919 leave : function() {
19920 clearTimeout(this.timeout);
19922 this.hoverState = 'out';
19924 if (!this.delay || !this.delay.hide) {
19929 this.timeout = setTimeout(function () {
19930 if (_t.hoverState == 'out') {
19933 }, this.delay.hide)
19937 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19938 * @param {string} (left|right|top|bottom) position
19940 show : function (on_el, placement)
19942 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19943 on_el = on_el || false; // default to false
19946 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19947 on_el = this.parent().el;
19948 } else if (this.over) {
19949 Roo.get(this.over);
19954 this.alignEl = Roo.get( on_el );
19957 this.render(document.body);
19963 if (this.title === false) {
19964 this.headerEl.hide();
19969 this.el.dom.style.display = 'block';
19972 if (this.alignEl) {
19973 this.updatePosition(this.placement, true);
19976 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19977 var es = this.el.getSize();
19978 var x = Roo.lib.Dom.getViewWidth()/2;
19979 var y = Roo.lib.Dom.getViewHeight()/2;
19980 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
19985 //var arrow = this.el.select('.arrow',true).first();
19986 //arrow.set(align[2],
19988 this.el.addClass('in');
19992 this.hoverState = 'in';
19995 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19996 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19997 this.maskEl.dom.style.display = 'block';
19998 this.maskEl.addClass('show');
20000 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20002 this.fireEvent('show', this);
20006 * fire this manually after loading a grid in the table for example
20007 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20008 * @param {Boolean} try and move it if we cant get right position.
20010 updatePosition : function(placement, try_move)
20012 // allow for calling with no parameters
20013 placement = placement ? placement : this.placement;
20014 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20016 this.el.removeClass([
20017 'fade','top','bottom', 'left', 'right','in',
20018 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20020 this.el.addClass(placement + ' bs-popover-' + placement);
20022 if (!this.alignEl ) {
20026 switch (placement) {
20028 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20029 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20030 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20031 //normal display... or moved up/down.
20032 this.el.setXY(offset);
20033 var xy = this.alignEl.getAnchorXY('tr', false);
20035 this.arrowEl.setXY(xy);
20038 // continue through...
20039 return this.updatePosition('left', false);
20043 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20044 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20045 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20046 //normal display... or moved up/down.
20047 this.el.setXY(offset);
20048 var xy = this.alignEl.getAnchorXY('tl', false);
20049 xy[0]-=10;xy[1]+=5; // << fix me
20050 this.arrowEl.setXY(xy);
20054 return this.updatePosition('right', false);
20057 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20058 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20059 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20060 //normal display... or moved up/down.
20061 this.el.setXY(offset);
20062 var xy = this.alignEl.getAnchorXY('t', false);
20063 xy[1]-=10; // << fix me
20064 this.arrowEl.setXY(xy);
20068 return this.updatePosition('bottom', false);
20071 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20072 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20073 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20074 //normal display... or moved up/down.
20075 this.el.setXY(offset);
20076 var xy = this.alignEl.getAnchorXY('b', false);
20077 xy[1]+=2; // << fix me
20078 this.arrowEl.setXY(xy);
20082 return this.updatePosition('top', false);
20093 this.el.setXY([0,0]);
20094 this.el.removeClass('in');
20096 this.hoverState = null;
20097 this.maskEl.hide(); // always..
20098 this.fireEvent('hide', this);
20104 Roo.apply(Roo.bootstrap.Popover, {
20107 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20108 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20109 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20110 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20115 clickHander : false,
20118 onMouseDown : function(e)
20120 if (!e.getTarget(".roo-popover")) {
20128 register : function(popup)
20130 if (!Roo.bootstrap.Popover.clickHandler) {
20131 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20133 // hide other popups.
20135 this.popups.push(popup);
20137 hideAll : function()
20139 this.popups.forEach(function(p) {
20147 * Card header - holder for the card header elements.
20152 * @class Roo.bootstrap.PopoverNav
20153 * @extends Roo.bootstrap.NavGroup
20154 * Bootstrap Popover header navigation class
20156 * Create a new Popover Header Navigation
20157 * @param {Object} config The config object
20160 Roo.bootstrap.PopoverNav = function(config){
20161 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20164 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20167 container_method : 'getPopoverHeader'
20185 * @class Roo.bootstrap.Progress
20186 * @extends Roo.bootstrap.Component
20187 * Bootstrap Progress class
20188 * @cfg {Boolean} striped striped of the progress bar
20189 * @cfg {Boolean} active animated of the progress bar
20193 * Create a new Progress
20194 * @param {Object} config The config object
20197 Roo.bootstrap.Progress = function(config){
20198 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20201 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20206 getAutoCreate : function(){
20214 cfg.cls += ' progress-striped';
20218 cfg.cls += ' active';
20237 * @class Roo.bootstrap.ProgressBar
20238 * @extends Roo.bootstrap.Component
20239 * Bootstrap ProgressBar class
20240 * @cfg {Number} aria_valuenow aria-value now
20241 * @cfg {Number} aria_valuemin aria-value min
20242 * @cfg {Number} aria_valuemax aria-value max
20243 * @cfg {String} label label for the progress bar
20244 * @cfg {String} panel (success | info | warning | danger )
20245 * @cfg {String} role role of the progress bar
20246 * @cfg {String} sr_only text
20250 * Create a new ProgressBar
20251 * @param {Object} config The config object
20254 Roo.bootstrap.ProgressBar = function(config){
20255 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20258 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20262 aria_valuemax : 100,
20268 getAutoCreate : function()
20273 cls: 'progress-bar',
20274 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20286 cfg.role = this.role;
20289 if(this.aria_valuenow){
20290 cfg['aria-valuenow'] = this.aria_valuenow;
20293 if(this.aria_valuemin){
20294 cfg['aria-valuemin'] = this.aria_valuemin;
20297 if(this.aria_valuemax){
20298 cfg['aria-valuemax'] = this.aria_valuemax;
20301 if(this.label && !this.sr_only){
20302 cfg.html = this.label;
20306 cfg.cls += ' progress-bar-' + this.panel;
20312 update : function(aria_valuenow)
20314 this.aria_valuenow = aria_valuenow;
20316 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20331 * @class Roo.bootstrap.TabGroup
20332 * @extends Roo.bootstrap.Column
20333 * Bootstrap Column class
20334 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20335 * @cfg {Boolean} carousel true to make the group behave like a carousel
20336 * @cfg {Boolean} bullets show bullets for the panels
20337 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20338 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20339 * @cfg {Boolean} showarrow (true|false) show arrow default true
20342 * Create a new TabGroup
20343 * @param {Object} config The config object
20346 Roo.bootstrap.TabGroup = function(config){
20347 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20349 this.navId = Roo.id();
20352 Roo.bootstrap.TabGroup.register(this);
20356 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20359 transition : false,
20364 slideOnTouch : false,
20367 getAutoCreate : function()
20369 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20371 cfg.cls += ' tab-content';
20373 if (this.carousel) {
20374 cfg.cls += ' carousel slide';
20377 cls : 'carousel-inner',
20381 if(this.bullets && !Roo.isTouch){
20384 cls : 'carousel-bullets',
20388 if(this.bullets_cls){
20389 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20396 cfg.cn[0].cn.push(bullets);
20399 if(this.showarrow){
20400 cfg.cn[0].cn.push({
20402 class : 'carousel-arrow',
20406 class : 'carousel-prev',
20410 class : 'fa fa-chevron-left'
20416 class : 'carousel-next',
20420 class : 'fa fa-chevron-right'
20433 initEvents: function()
20435 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20436 // this.el.on("touchstart", this.onTouchStart, this);
20439 if(this.autoslide){
20442 this.slideFn = window.setInterval(function() {
20443 _this.showPanelNext();
20447 if(this.showarrow){
20448 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20449 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20455 // onTouchStart : function(e, el, o)
20457 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20461 // this.showPanelNext();
20465 getChildContainer : function()
20467 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20471 * register a Navigation item
20472 * @param {Roo.bootstrap.NavItem} the navitem to add
20474 register : function(item)
20476 this.tabs.push( item);
20477 item.navId = this.navId; // not really needed..
20482 getActivePanel : function()
20485 Roo.each(this.tabs, function(t) {
20495 getPanelByName : function(n)
20498 Roo.each(this.tabs, function(t) {
20499 if (t.tabId == n) {
20507 indexOfPanel : function(p)
20510 Roo.each(this.tabs, function(t,i) {
20511 if (t.tabId == p.tabId) {
20520 * show a specific panel
20521 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20522 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20524 showPanel : function (pan)
20526 if(this.transition || typeof(pan) == 'undefined'){
20527 Roo.log("waiting for the transitionend");
20531 if (typeof(pan) == 'number') {
20532 pan = this.tabs[pan];
20535 if (typeof(pan) == 'string') {
20536 pan = this.getPanelByName(pan);
20539 var cur = this.getActivePanel();
20542 Roo.log('pan or acitve pan is undefined');
20546 if (pan.tabId == this.getActivePanel().tabId) {
20550 if (false === cur.fireEvent('beforedeactivate')) {
20554 if(this.bullets > 0 && !Roo.isTouch){
20555 this.setActiveBullet(this.indexOfPanel(pan));
20558 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20560 //class="carousel-item carousel-item-next carousel-item-left"
20562 this.transition = true;
20563 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20564 var lr = dir == 'next' ? 'left' : 'right';
20565 pan.el.addClass(dir); // or prev
20566 pan.el.addClass('carousel-item-' + dir); // or prev
20567 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20568 cur.el.addClass(lr); // or right
20569 pan.el.addClass(lr);
20570 cur.el.addClass('carousel-item-' +lr); // or right
20571 pan.el.addClass('carousel-item-' +lr);
20575 cur.el.on('transitionend', function() {
20576 Roo.log("trans end?");
20578 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20579 pan.setActive(true);
20581 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20582 cur.setActive(false);
20584 _this.transition = false;
20586 }, this, { single: true } );
20591 cur.setActive(false);
20592 pan.setActive(true);
20597 showPanelNext : function()
20599 var i = this.indexOfPanel(this.getActivePanel());
20601 if (i >= this.tabs.length - 1 && !this.autoslide) {
20605 if (i >= this.tabs.length - 1 && this.autoslide) {
20609 this.showPanel(this.tabs[i+1]);
20612 showPanelPrev : function()
20614 var i = this.indexOfPanel(this.getActivePanel());
20616 if (i < 1 && !this.autoslide) {
20620 if (i < 1 && this.autoslide) {
20621 i = this.tabs.length;
20624 this.showPanel(this.tabs[i-1]);
20628 addBullet: function()
20630 if(!this.bullets || Roo.isTouch){
20633 var ctr = this.el.select('.carousel-bullets',true).first();
20634 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20635 var bullet = ctr.createChild({
20636 cls : 'bullet bullet-' + i
20637 },ctr.dom.lastChild);
20642 bullet.on('click', (function(e, el, o, ii, t){
20644 e.preventDefault();
20646 this.showPanel(ii);
20648 if(this.autoslide && this.slideFn){
20649 clearInterval(this.slideFn);
20650 this.slideFn = window.setInterval(function() {
20651 _this.showPanelNext();
20655 }).createDelegate(this, [i, bullet], true));
20660 setActiveBullet : function(i)
20666 Roo.each(this.el.select('.bullet', true).elements, function(el){
20667 el.removeClass('selected');
20670 var bullet = this.el.select('.bullet-' + i, true).first();
20676 bullet.addClass('selected');
20687 Roo.apply(Roo.bootstrap.TabGroup, {
20691 * register a Navigation Group
20692 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20694 register : function(navgrp)
20696 this.groups[navgrp.navId] = navgrp;
20700 * fetch a Navigation Group based on the navigation ID
20701 * if one does not exist , it will get created.
20702 * @param {string} the navgroup to add
20703 * @returns {Roo.bootstrap.NavGroup} the navgroup
20705 get: function(navId) {
20706 if (typeof(this.groups[navId]) == 'undefined') {
20707 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20709 return this.groups[navId] ;
20724 * @class Roo.bootstrap.TabPanel
20725 * @extends Roo.bootstrap.Component
20726 * Bootstrap TabPanel class
20727 * @cfg {Boolean} active panel active
20728 * @cfg {String} html panel content
20729 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20730 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20731 * @cfg {String} href click to link..
20732 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20736 * Create a new TabPanel
20737 * @param {Object} config The config object
20740 Roo.bootstrap.TabPanel = function(config){
20741 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20745 * Fires when the active status changes
20746 * @param {Roo.bootstrap.TabPanel} this
20747 * @param {Boolean} state the new state
20752 * @event beforedeactivate
20753 * Fires before a tab is de-activated - can be used to do validation on a form.
20754 * @param {Roo.bootstrap.TabPanel} this
20755 * @return {Boolean} false if there is an error
20758 'beforedeactivate': true
20761 this.tabId = this.tabId || Roo.id();
20765 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20772 touchSlide : false,
20773 getAutoCreate : function(){
20778 // item is needed for carousel - not sure if it has any effect otherwise
20779 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20780 html: this.html || ''
20784 cfg.cls += ' active';
20788 cfg.tabId = this.tabId;
20796 initEvents: function()
20798 var p = this.parent();
20800 this.navId = this.navId || p.navId;
20802 if (typeof(this.navId) != 'undefined') {
20803 // not really needed.. but just in case.. parent should be a NavGroup.
20804 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20808 var i = tg.tabs.length - 1;
20810 if(this.active && tg.bullets > 0 && i < tg.bullets){
20811 tg.setActiveBullet(i);
20815 this.el.on('click', this.onClick, this);
20817 if(Roo.isTouch && this.touchSlide){
20818 this.el.on("touchstart", this.onTouchStart, this);
20819 this.el.on("touchmove", this.onTouchMove, this);
20820 this.el.on("touchend", this.onTouchEnd, this);
20825 onRender : function(ct, position)
20827 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20830 setActive : function(state)
20832 Roo.log("panel - set active " + this.tabId + "=" + state);
20834 this.active = state;
20836 this.el.removeClass('active');
20838 } else if (!this.el.hasClass('active')) {
20839 this.el.addClass('active');
20842 this.fireEvent('changed', this, state);
20845 onClick : function(e)
20847 e.preventDefault();
20849 if(!this.href.length){
20853 window.location.href = this.href;
20862 onTouchStart : function(e)
20864 this.swiping = false;
20866 this.startX = e.browserEvent.touches[0].clientX;
20867 this.startY = e.browserEvent.touches[0].clientY;
20870 onTouchMove : function(e)
20872 this.swiping = true;
20874 this.endX = e.browserEvent.touches[0].clientX;
20875 this.endY = e.browserEvent.touches[0].clientY;
20878 onTouchEnd : function(e)
20885 var tabGroup = this.parent();
20887 if(this.endX > this.startX){ // swiping right
20888 tabGroup.showPanelPrev();
20892 if(this.startX > this.endX){ // swiping left
20893 tabGroup.showPanelNext();
20912 * @class Roo.bootstrap.DateField
20913 * @extends Roo.bootstrap.Input
20914 * Bootstrap DateField class
20915 * @cfg {Number} weekStart default 0
20916 * @cfg {String} viewMode default empty, (months|years)
20917 * @cfg {String} minViewMode default empty, (months|years)
20918 * @cfg {Number} startDate default -Infinity
20919 * @cfg {Number} endDate default Infinity
20920 * @cfg {Boolean} todayHighlight default false
20921 * @cfg {Boolean} todayBtn default false
20922 * @cfg {Boolean} calendarWeeks default false
20923 * @cfg {Object} daysOfWeekDisabled default empty
20924 * @cfg {Boolean} singleMode default false (true | false)
20926 * @cfg {Boolean} keyboardNavigation default true
20927 * @cfg {String} language default en
20930 * Create a new DateField
20931 * @param {Object} config The config object
20934 Roo.bootstrap.DateField = function(config){
20935 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20939 * Fires when this field show.
20940 * @param {Roo.bootstrap.DateField} this
20941 * @param {Mixed} date The date value
20946 * Fires when this field hide.
20947 * @param {Roo.bootstrap.DateField} this
20948 * @param {Mixed} date The date value
20953 * Fires when select a date.
20954 * @param {Roo.bootstrap.DateField} this
20955 * @param {Mixed} date The date value
20959 * @event beforeselect
20960 * Fires when before select a date.
20961 * @param {Roo.bootstrap.DateField} this
20962 * @param {Mixed} date The date value
20964 beforeselect : true
20968 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20971 * @cfg {String} format
20972 * The default date format string which can be overriden for localization support. The format must be
20973 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20977 * @cfg {String} altFormats
20978 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20979 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20981 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20989 todayHighlight : false,
20995 keyboardNavigation: true,
20997 calendarWeeks: false,
20999 startDate: -Infinity,
21003 daysOfWeekDisabled: [],
21007 singleMode : false,
21009 UTCDate: function()
21011 return new Date(Date.UTC.apply(Date, arguments));
21014 UTCToday: function()
21016 var today = new Date();
21017 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21020 getDate: function() {
21021 var d = this.getUTCDate();
21022 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21025 getUTCDate: function() {
21029 setDate: function(d) {
21030 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21033 setUTCDate: function(d) {
21035 this.setValue(this.formatDate(this.date));
21038 onRender: function(ct, position)
21041 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21043 this.language = this.language || 'en';
21044 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21045 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21047 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21048 this.format = this.format || 'm/d/y';
21049 this.isInline = false;
21050 this.isInput = true;
21051 this.component = this.el.select('.add-on', true).first() || false;
21052 this.component = (this.component && this.component.length === 0) ? false : this.component;
21053 this.hasInput = this.component && this.inputEl().length;
21055 if (typeof(this.minViewMode === 'string')) {
21056 switch (this.minViewMode) {
21058 this.minViewMode = 1;
21061 this.minViewMode = 2;
21064 this.minViewMode = 0;
21069 if (typeof(this.viewMode === 'string')) {
21070 switch (this.viewMode) {
21083 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21085 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21087 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21089 this.picker().on('mousedown', this.onMousedown, this);
21090 this.picker().on('click', this.onClick, this);
21092 this.picker().addClass('datepicker-dropdown');
21094 this.startViewMode = this.viewMode;
21096 if(this.singleMode){
21097 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21098 v.setVisibilityMode(Roo.Element.DISPLAY);
21102 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21103 v.setStyle('width', '189px');
21107 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21108 if(!this.calendarWeeks){
21113 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21114 v.attr('colspan', function(i, val){
21115 return parseInt(val) + 1;
21120 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21122 this.setStartDate(this.startDate);
21123 this.setEndDate(this.endDate);
21125 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21132 if(this.isInline) {
21137 picker : function()
21139 return this.pickerEl;
21140 // return this.el.select('.datepicker', true).first();
21143 fillDow: function()
21145 var dowCnt = this.weekStart;
21154 if(this.calendarWeeks){
21162 while (dowCnt < this.weekStart + 7) {
21166 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21170 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21173 fillMonths: function()
21176 var months = this.picker().select('>.datepicker-months td', true).first();
21178 months.dom.innerHTML = '';
21184 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21187 months.createChild(month);
21194 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;
21196 if (this.date < this.startDate) {
21197 this.viewDate = new Date(this.startDate);
21198 } else if (this.date > this.endDate) {
21199 this.viewDate = new Date(this.endDate);
21201 this.viewDate = new Date(this.date);
21209 var d = new Date(this.viewDate),
21210 year = d.getUTCFullYear(),
21211 month = d.getUTCMonth(),
21212 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21213 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21214 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21215 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21216 currentDate = this.date && this.date.valueOf(),
21217 today = this.UTCToday();
21219 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21221 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21223 // this.picker.select('>tfoot th.today').
21224 // .text(dates[this.language].today)
21225 // .toggle(this.todayBtn !== false);
21227 this.updateNavArrows();
21230 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21232 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21234 prevMonth.setUTCDate(day);
21236 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21238 var nextMonth = new Date(prevMonth);
21240 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21242 nextMonth = nextMonth.valueOf();
21244 var fillMonths = false;
21246 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21248 while(prevMonth.valueOf() <= nextMonth) {
21251 if (prevMonth.getUTCDay() === this.weekStart) {
21253 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21261 if(this.calendarWeeks){
21262 // ISO 8601: First week contains first thursday.
21263 // ISO also states week starts on Monday, but we can be more abstract here.
21265 // Start of current week: based on weekstart/current date
21266 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21267 // Thursday of this week
21268 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21269 // First Thursday of year, year from thursday
21270 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21271 // Calendar week: ms between thursdays, div ms per day, div 7 days
21272 calWeek = (th - yth) / 864e5 / 7 + 1;
21274 fillMonths.cn.push({
21282 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21284 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21287 if (this.todayHighlight &&
21288 prevMonth.getUTCFullYear() == today.getFullYear() &&
21289 prevMonth.getUTCMonth() == today.getMonth() &&
21290 prevMonth.getUTCDate() == today.getDate()) {
21291 clsName += ' today';
21294 if (currentDate && prevMonth.valueOf() === currentDate) {
21295 clsName += ' active';
21298 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21299 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21300 clsName += ' disabled';
21303 fillMonths.cn.push({
21305 cls: 'day ' + clsName,
21306 html: prevMonth.getDate()
21309 prevMonth.setDate(prevMonth.getDate()+1);
21312 var currentYear = this.date && this.date.getUTCFullYear();
21313 var currentMonth = this.date && this.date.getUTCMonth();
21315 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21317 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21318 v.removeClass('active');
21320 if(currentYear === year && k === currentMonth){
21321 v.addClass('active');
21324 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21325 v.addClass('disabled');
21331 year = parseInt(year/10, 10) * 10;
21333 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21335 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21338 for (var i = -1; i < 11; i++) {
21339 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21341 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21349 showMode: function(dir)
21352 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21355 Roo.each(this.picker().select('>div',true).elements, function(v){
21356 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21359 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21364 if(this.isInline) {
21368 this.picker().removeClass(['bottom', 'top']);
21370 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21372 * place to the top of element!
21376 this.picker().addClass('top');
21377 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21382 this.picker().addClass('bottom');
21384 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21387 parseDate : function(value)
21389 if(!value || value instanceof Date){
21392 var v = Date.parseDate(value, this.format);
21393 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21394 v = Date.parseDate(value, 'Y-m-d');
21396 if(!v && this.altFormats){
21397 if(!this.altFormatsArray){
21398 this.altFormatsArray = this.altFormats.split("|");
21400 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21401 v = Date.parseDate(value, this.altFormatsArray[i]);
21407 formatDate : function(date, fmt)
21409 return (!date || !(date instanceof Date)) ?
21410 date : date.dateFormat(fmt || this.format);
21413 onFocus : function()
21415 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21419 onBlur : function()
21421 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21423 var d = this.inputEl().getValue();
21430 showPopup : function()
21432 this.picker().show();
21436 this.fireEvent('showpopup', this, this.date);
21439 hidePopup : function()
21441 if(this.isInline) {
21444 this.picker().hide();
21445 this.viewMode = this.startViewMode;
21448 this.fireEvent('hidepopup', this, this.date);
21452 onMousedown: function(e)
21454 e.stopPropagation();
21455 e.preventDefault();
21460 Roo.bootstrap.DateField.superclass.keyup.call(this);
21464 setValue: function(v)
21466 if(this.fireEvent('beforeselect', this, v) !== false){
21467 var d = new Date(this.parseDate(v) ).clearTime();
21469 if(isNaN(d.getTime())){
21470 this.date = this.viewDate = '';
21471 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21475 v = this.formatDate(d);
21477 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21479 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21483 this.fireEvent('select', this, this.date);
21487 getValue: function()
21489 return this.formatDate(this.date);
21492 fireKey: function(e)
21494 if (!this.picker().isVisible()){
21495 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21501 var dateChanged = false,
21503 newDate, newViewDate;
21508 e.preventDefault();
21512 if (!this.keyboardNavigation) {
21515 dir = e.keyCode == 37 ? -1 : 1;
21518 newDate = this.moveYear(this.date, dir);
21519 newViewDate = this.moveYear(this.viewDate, dir);
21520 } else if (e.shiftKey){
21521 newDate = this.moveMonth(this.date, dir);
21522 newViewDate = this.moveMonth(this.viewDate, dir);
21524 newDate = new Date(this.date);
21525 newDate.setUTCDate(this.date.getUTCDate() + dir);
21526 newViewDate = new Date(this.viewDate);
21527 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21529 if (this.dateWithinRange(newDate)){
21530 this.date = newDate;
21531 this.viewDate = newViewDate;
21532 this.setValue(this.formatDate(this.date));
21534 e.preventDefault();
21535 dateChanged = true;
21540 if (!this.keyboardNavigation) {
21543 dir = e.keyCode == 38 ? -1 : 1;
21545 newDate = this.moveYear(this.date, dir);
21546 newViewDate = this.moveYear(this.viewDate, dir);
21547 } else if (e.shiftKey){
21548 newDate = this.moveMonth(this.date, dir);
21549 newViewDate = this.moveMonth(this.viewDate, dir);
21551 newDate = new Date(this.date);
21552 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21553 newViewDate = new Date(this.viewDate);
21554 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21556 if (this.dateWithinRange(newDate)){
21557 this.date = newDate;
21558 this.viewDate = newViewDate;
21559 this.setValue(this.formatDate(this.date));
21561 e.preventDefault();
21562 dateChanged = true;
21566 this.setValue(this.formatDate(this.date));
21568 e.preventDefault();
21571 this.setValue(this.formatDate(this.date));
21585 onClick: function(e)
21587 e.stopPropagation();
21588 e.preventDefault();
21590 var target = e.getTarget();
21592 if(target.nodeName.toLowerCase() === 'i'){
21593 target = Roo.get(target).dom.parentNode;
21596 var nodeName = target.nodeName;
21597 var className = target.className;
21598 var html = target.innerHTML;
21599 //Roo.log(nodeName);
21601 switch(nodeName.toLowerCase()) {
21603 switch(className) {
21609 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21610 switch(this.viewMode){
21612 this.viewDate = this.moveMonth(this.viewDate, dir);
21616 this.viewDate = this.moveYear(this.viewDate, dir);
21622 var date = new Date();
21623 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21625 this.setValue(this.formatDate(this.date));
21632 if (className.indexOf('disabled') < 0) {
21633 this.viewDate.setUTCDate(1);
21634 if (className.indexOf('month') > -1) {
21635 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21637 var year = parseInt(html, 10) || 0;
21638 this.viewDate.setUTCFullYear(year);
21642 if(this.singleMode){
21643 this.setValue(this.formatDate(this.viewDate));
21654 //Roo.log(className);
21655 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21656 var day = parseInt(html, 10) || 1;
21657 var year = (this.viewDate || new Date()).getUTCFullYear(),
21658 month = (this.viewDate || new Date()).getUTCMonth();
21660 if (className.indexOf('old') > -1) {
21667 } else if (className.indexOf('new') > -1) {
21675 //Roo.log([year,month,day]);
21676 this.date = this.UTCDate(year, month, day,0,0,0,0);
21677 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21679 //Roo.log(this.formatDate(this.date));
21680 this.setValue(this.formatDate(this.date));
21687 setStartDate: function(startDate)
21689 this.startDate = startDate || -Infinity;
21690 if (this.startDate !== -Infinity) {
21691 this.startDate = this.parseDate(this.startDate);
21694 this.updateNavArrows();
21697 setEndDate: function(endDate)
21699 this.endDate = endDate || Infinity;
21700 if (this.endDate !== Infinity) {
21701 this.endDate = this.parseDate(this.endDate);
21704 this.updateNavArrows();
21707 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21709 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21710 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21711 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21713 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21714 return parseInt(d, 10);
21717 this.updateNavArrows();
21720 updateNavArrows: function()
21722 if(this.singleMode){
21726 var d = new Date(this.viewDate),
21727 year = d.getUTCFullYear(),
21728 month = d.getUTCMonth();
21730 Roo.each(this.picker().select('.prev', true).elements, function(v){
21732 switch (this.viewMode) {
21735 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21741 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21748 Roo.each(this.picker().select('.next', true).elements, function(v){
21750 switch (this.viewMode) {
21753 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21759 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21767 moveMonth: function(date, dir)
21772 var new_date = new Date(date.valueOf()),
21773 day = new_date.getUTCDate(),
21774 month = new_date.getUTCMonth(),
21775 mag = Math.abs(dir),
21777 dir = dir > 0 ? 1 : -1;
21780 // If going back one month, make sure month is not current month
21781 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21783 return new_date.getUTCMonth() == month;
21785 // If going forward one month, make sure month is as expected
21786 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21788 return new_date.getUTCMonth() != new_month;
21790 new_month = month + dir;
21791 new_date.setUTCMonth(new_month);
21792 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21793 if (new_month < 0 || new_month > 11) {
21794 new_month = (new_month + 12) % 12;
21797 // For magnitudes >1, move one month at a time...
21798 for (var i=0; i<mag; i++) {
21799 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21800 new_date = this.moveMonth(new_date, dir);
21802 // ...then reset the day, keeping it in the new month
21803 new_month = new_date.getUTCMonth();
21804 new_date.setUTCDate(day);
21806 return new_month != new_date.getUTCMonth();
21809 // Common date-resetting loop -- if date is beyond end of month, make it
21812 new_date.setUTCDate(--day);
21813 new_date.setUTCMonth(new_month);
21818 moveYear: function(date, dir)
21820 return this.moveMonth(date, dir*12);
21823 dateWithinRange: function(date)
21825 return date >= this.startDate && date <= this.endDate;
21831 this.picker().remove();
21834 validateValue : function(value)
21836 if(this.getVisibilityEl().hasClass('hidden')){
21840 if(value.length < 1) {
21841 if(this.allowBlank){
21847 if(value.length < this.minLength){
21850 if(value.length > this.maxLength){
21854 var vt = Roo.form.VTypes;
21855 if(!vt[this.vtype](value, this)){
21859 if(typeof this.validator == "function"){
21860 var msg = this.validator(value);
21866 if(this.regex && !this.regex.test(value)){
21870 if(typeof(this.parseDate(value)) == 'undefined'){
21874 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21878 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21888 this.date = this.viewDate = '';
21890 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21895 Roo.apply(Roo.bootstrap.DateField, {
21906 html: '<i class="fa fa-arrow-left"/>'
21916 html: '<i class="fa fa-arrow-right"/>'
21958 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21959 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21960 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21961 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21962 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21975 navFnc: 'FullYear',
21980 navFnc: 'FullYear',
21985 Roo.apply(Roo.bootstrap.DateField, {
21989 cls: 'datepicker dropdown-menu roo-dynamic shadow',
21993 cls: 'datepicker-days',
21997 cls: 'table-condensed',
21999 Roo.bootstrap.DateField.head,
22003 Roo.bootstrap.DateField.footer
22010 cls: 'datepicker-months',
22014 cls: 'table-condensed',
22016 Roo.bootstrap.DateField.head,
22017 Roo.bootstrap.DateField.content,
22018 Roo.bootstrap.DateField.footer
22025 cls: 'datepicker-years',
22029 cls: 'table-condensed',
22031 Roo.bootstrap.DateField.head,
22032 Roo.bootstrap.DateField.content,
22033 Roo.bootstrap.DateField.footer
22052 * @class Roo.bootstrap.TimeField
22053 * @extends Roo.bootstrap.Input
22054 * Bootstrap DateField class
22058 * Create a new TimeField
22059 * @param {Object} config The config object
22062 Roo.bootstrap.TimeField = function(config){
22063 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22067 * Fires when this field show.
22068 * @param {Roo.bootstrap.DateField} thisthis
22069 * @param {Mixed} date The date value
22074 * Fires when this field hide.
22075 * @param {Roo.bootstrap.DateField} this
22076 * @param {Mixed} date The date value
22081 * Fires when select a date.
22082 * @param {Roo.bootstrap.DateField} this
22083 * @param {Mixed} date The date value
22089 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22092 * @cfg {String} format
22093 * The default time format string which can be overriden for localization support. The format must be
22094 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22098 getAutoCreate : function()
22100 this.after = '<i class="fa far fa-clock"></i>';
22101 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22105 onRender: function(ct, position)
22108 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22110 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22112 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22114 this.pop = this.picker().select('>.datepicker-time',true).first();
22115 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22117 this.picker().on('mousedown', this.onMousedown, this);
22118 this.picker().on('click', this.onClick, this);
22120 this.picker().addClass('datepicker-dropdown');
22125 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22126 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22127 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22128 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22129 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22130 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22134 fireKey: function(e){
22135 if (!this.picker().isVisible()){
22136 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22142 e.preventDefault();
22150 this.onTogglePeriod();
22153 this.onIncrementMinutes();
22156 this.onDecrementMinutes();
22165 onClick: function(e) {
22166 e.stopPropagation();
22167 e.preventDefault();
22170 picker : function()
22172 return this.pickerEl;
22175 fillTime: function()
22177 var time = this.pop.select('tbody', true).first();
22179 time.dom.innerHTML = '';
22194 cls: 'hours-up fa fas fa-chevron-up'
22214 cls: 'minutes-up fa fas fa-chevron-up'
22235 cls: 'timepicker-hour',
22250 cls: 'timepicker-minute',
22265 cls: 'btn btn-primary period',
22287 cls: 'hours-down fa fas fa-chevron-down'
22307 cls: 'minutes-down fa fas fa-chevron-down'
22325 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22332 var hours = this.time.getHours();
22333 var minutes = this.time.getMinutes();
22346 hours = hours - 12;
22350 hours = '0' + hours;
22354 minutes = '0' + minutes;
22357 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22358 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22359 this.pop.select('button', true).first().dom.innerHTML = period;
22365 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22367 var cls = ['bottom'];
22369 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22376 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22380 //this.picker().setXY(20000,20000);
22381 this.picker().addClass(cls.join('-'));
22385 Roo.each(cls, function(c){
22390 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22391 //_this.picker().setTop(_this.inputEl().getHeight());
22395 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22397 //_this.picker().setTop(0 - _this.picker().getHeight());
22402 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22406 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22414 onFocus : function()
22416 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22420 onBlur : function()
22422 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22428 this.picker().show();
22433 this.fireEvent('show', this, this.date);
22438 this.picker().hide();
22441 this.fireEvent('hide', this, this.date);
22444 setTime : function()
22447 this.setValue(this.time.format(this.format));
22449 this.fireEvent('select', this, this.date);
22454 onMousedown: function(e){
22455 e.stopPropagation();
22456 e.preventDefault();
22459 onIncrementHours: function()
22461 Roo.log('onIncrementHours');
22462 this.time = this.time.add(Date.HOUR, 1);
22467 onDecrementHours: function()
22469 Roo.log('onDecrementHours');
22470 this.time = this.time.add(Date.HOUR, -1);
22474 onIncrementMinutes: function()
22476 Roo.log('onIncrementMinutes');
22477 this.time = this.time.add(Date.MINUTE, 1);
22481 onDecrementMinutes: function()
22483 Roo.log('onDecrementMinutes');
22484 this.time = this.time.add(Date.MINUTE, -1);
22488 onTogglePeriod: function()
22490 Roo.log('onTogglePeriod');
22491 this.time = this.time.add(Date.HOUR, 12);
22499 Roo.apply(Roo.bootstrap.TimeField, {
22503 cls: 'datepicker dropdown-menu',
22507 cls: 'datepicker-time',
22511 cls: 'table-condensed',
22540 cls: 'btn btn-info ok',
22568 * @class Roo.bootstrap.MonthField
22569 * @extends Roo.bootstrap.Input
22570 * Bootstrap MonthField class
22572 * @cfg {String} language default en
22575 * Create a new MonthField
22576 * @param {Object} config The config object
22579 Roo.bootstrap.MonthField = function(config){
22580 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22585 * Fires when this field show.
22586 * @param {Roo.bootstrap.MonthField} this
22587 * @param {Mixed} date The date value
22592 * Fires when this field hide.
22593 * @param {Roo.bootstrap.MonthField} this
22594 * @param {Mixed} date The date value
22599 * Fires when select a date.
22600 * @param {Roo.bootstrap.MonthField} this
22601 * @param {String} oldvalue The old value
22602 * @param {String} newvalue The new value
22608 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22610 onRender: function(ct, position)
22613 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22615 this.language = this.language || 'en';
22616 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22617 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22619 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22620 this.isInline = false;
22621 this.isInput = true;
22622 this.component = this.el.select('.add-on', true).first() || false;
22623 this.component = (this.component && this.component.length === 0) ? false : this.component;
22624 this.hasInput = this.component && this.inputEL().length;
22626 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22628 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22630 this.picker().on('mousedown', this.onMousedown, this);
22631 this.picker().on('click', this.onClick, this);
22633 this.picker().addClass('datepicker-dropdown');
22635 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22636 v.setStyle('width', '189px');
22643 if(this.isInline) {
22649 setValue: function(v, suppressEvent)
22651 var o = this.getValue();
22653 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22657 if(suppressEvent !== true){
22658 this.fireEvent('select', this, o, v);
22663 getValue: function()
22668 onClick: function(e)
22670 e.stopPropagation();
22671 e.preventDefault();
22673 var target = e.getTarget();
22675 if(target.nodeName.toLowerCase() === 'i'){
22676 target = Roo.get(target).dom.parentNode;
22679 var nodeName = target.nodeName;
22680 var className = target.className;
22681 var html = target.innerHTML;
22683 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22687 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22689 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22695 picker : function()
22697 return this.pickerEl;
22700 fillMonths: function()
22703 var months = this.picker().select('>.datepicker-months td', true).first();
22705 months.dom.innerHTML = '';
22711 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22714 months.createChild(month);
22723 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22724 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22727 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22728 e.removeClass('active');
22730 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22731 e.addClass('active');
22738 if(this.isInline) {
22742 this.picker().removeClass(['bottom', 'top']);
22744 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22746 * place to the top of element!
22750 this.picker().addClass('top');
22751 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22756 this.picker().addClass('bottom');
22758 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22761 onFocus : function()
22763 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22767 onBlur : function()
22769 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22771 var d = this.inputEl().getValue();
22780 this.picker().show();
22781 this.picker().select('>.datepicker-months', true).first().show();
22785 this.fireEvent('show', this, this.date);
22790 if(this.isInline) {
22793 this.picker().hide();
22794 this.fireEvent('hide', this, this.date);
22798 onMousedown: function(e)
22800 e.stopPropagation();
22801 e.preventDefault();
22806 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22810 fireKey: function(e)
22812 if (!this.picker().isVisible()){
22813 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22824 e.preventDefault();
22828 dir = e.keyCode == 37 ? -1 : 1;
22830 this.vIndex = this.vIndex + dir;
22832 if(this.vIndex < 0){
22836 if(this.vIndex > 11){
22840 if(isNaN(this.vIndex)){
22844 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22850 dir = e.keyCode == 38 ? -1 : 1;
22852 this.vIndex = this.vIndex + dir * 4;
22854 if(this.vIndex < 0){
22858 if(this.vIndex > 11){
22862 if(isNaN(this.vIndex)){
22866 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22871 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22872 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22876 e.preventDefault();
22879 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22880 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22896 this.picker().remove();
22901 Roo.apply(Roo.bootstrap.MonthField, {
22920 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22921 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22926 Roo.apply(Roo.bootstrap.MonthField, {
22930 cls: 'datepicker dropdown-menu roo-dynamic',
22934 cls: 'datepicker-months',
22938 cls: 'table-condensed',
22940 Roo.bootstrap.DateField.content
22960 * @class Roo.bootstrap.CheckBox
22961 * @extends Roo.bootstrap.Input
22962 * Bootstrap CheckBox class
22964 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22965 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22966 * @cfg {String} boxLabel The text that appears beside the checkbox
22967 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22968 * @cfg {Boolean} checked initnal the element
22969 * @cfg {Boolean} inline inline the element (default false)
22970 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22971 * @cfg {String} tooltip label tooltip
22974 * Create a new CheckBox
22975 * @param {Object} config The config object
22978 Roo.bootstrap.CheckBox = function(config){
22979 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22984 * Fires when the element is checked or unchecked.
22985 * @param {Roo.bootstrap.CheckBox} this This input
22986 * @param {Boolean} checked The new checked value
22991 * Fires when the element is click.
22992 * @param {Roo.bootstrap.CheckBox} this This input
22999 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23001 inputType: 'checkbox',
23010 // checkbox success does not make any sense really..
23015 getAutoCreate : function()
23017 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23023 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23026 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23032 type : this.inputType,
23033 value : this.inputValue,
23034 cls : 'roo-' + this.inputType, //'form-box',
23035 placeholder : this.placeholder || ''
23039 if(this.inputType != 'radio'){
23043 cls : 'roo-hidden-value',
23044 value : this.checked ? this.inputValue : this.valueOff
23049 if (this.weight) { // Validity check?
23050 cfg.cls += " " + this.inputType + "-" + this.weight;
23053 if (this.disabled) {
23054 input.disabled=true;
23058 input.checked = this.checked;
23063 input.name = this.name;
23065 if(this.inputType != 'radio'){
23066 hidden.name = this.name;
23067 input.name = '_hidden_' + this.name;
23072 input.cls += ' input-' + this.size;
23077 ['xs','sm','md','lg'].map(function(size){
23078 if (settings[size]) {
23079 cfg.cls += ' col-' + size + '-' + settings[size];
23083 var inputblock = input;
23085 if (this.before || this.after) {
23088 cls : 'input-group',
23093 inputblock.cn.push({
23095 cls : 'input-group-addon',
23100 inputblock.cn.push(input);
23102 if(this.inputType != 'radio'){
23103 inputblock.cn.push(hidden);
23107 inputblock.cn.push({
23109 cls : 'input-group-addon',
23115 var boxLabelCfg = false;
23121 //'for': id, // box label is handled by onclick - so no for...
23123 html: this.boxLabel
23126 boxLabelCfg.tooltip = this.tooltip;
23132 if (align ==='left' && this.fieldLabel.length) {
23133 // Roo.log("left and has label");
23138 cls : 'control-label',
23139 html : this.fieldLabel
23150 cfg.cn[1].cn.push(boxLabelCfg);
23153 if(this.labelWidth > 12){
23154 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23157 if(this.labelWidth < 13 && this.labelmd == 0){
23158 this.labelmd = this.labelWidth;
23161 if(this.labellg > 0){
23162 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23163 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23166 if(this.labelmd > 0){
23167 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23168 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23171 if(this.labelsm > 0){
23172 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23173 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23176 if(this.labelxs > 0){
23177 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23178 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23181 } else if ( this.fieldLabel.length) {
23182 // Roo.log(" label");
23186 tag: this.boxLabel ? 'span' : 'label',
23188 cls: 'control-label box-input-label',
23189 //cls : 'input-group-addon',
23190 html : this.fieldLabel
23197 cfg.cn.push(boxLabelCfg);
23202 // Roo.log(" no label && no align");
23203 cfg.cn = [ inputblock ] ;
23205 cfg.cn.push(boxLabelCfg);
23213 if(this.inputType != 'radio'){
23214 cfg.cn.push(hidden);
23222 * return the real input element.
23224 inputEl: function ()
23226 return this.el.select('input.roo-' + this.inputType,true).first();
23228 hiddenEl: function ()
23230 return this.el.select('input.roo-hidden-value',true).first();
23233 labelEl: function()
23235 return this.el.select('label.control-label',true).first();
23237 /* depricated... */
23241 return this.labelEl();
23244 boxLabelEl: function()
23246 return this.el.select('label.box-label',true).first();
23249 initEvents : function()
23251 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23253 this.inputEl().on('click', this.onClick, this);
23255 if (this.boxLabel) {
23256 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23259 this.startValue = this.getValue();
23262 Roo.bootstrap.CheckBox.register(this);
23266 onClick : function(e)
23268 if(this.fireEvent('click', this, e) !== false){
23269 this.setChecked(!this.checked);
23274 setChecked : function(state,suppressEvent)
23276 this.startValue = this.getValue();
23278 if(this.inputType == 'radio'){
23280 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23281 e.dom.checked = false;
23284 this.inputEl().dom.checked = true;
23286 this.inputEl().dom.value = this.inputValue;
23288 if(suppressEvent !== true){
23289 this.fireEvent('check', this, true);
23297 this.checked = state;
23299 this.inputEl().dom.checked = state;
23302 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23304 if(suppressEvent !== true){
23305 this.fireEvent('check', this, state);
23311 getValue : function()
23313 if(this.inputType == 'radio'){
23314 return this.getGroupValue();
23317 return this.hiddenEl().dom.value;
23321 getGroupValue : function()
23323 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23327 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23330 setValue : function(v,suppressEvent)
23332 if(this.inputType == 'radio'){
23333 this.setGroupValue(v, suppressEvent);
23337 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23342 setGroupValue : function(v, suppressEvent)
23344 this.startValue = this.getValue();
23346 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23347 e.dom.checked = false;
23349 if(e.dom.value == v){
23350 e.dom.checked = true;
23354 if(suppressEvent !== true){
23355 this.fireEvent('check', this, true);
23363 validate : function()
23365 if(this.getVisibilityEl().hasClass('hidden')){
23371 (this.inputType == 'radio' && this.validateRadio()) ||
23372 (this.inputType == 'checkbox' && this.validateCheckbox())
23378 this.markInvalid();
23382 validateRadio : function()
23384 if(this.getVisibilityEl().hasClass('hidden')){
23388 if(this.allowBlank){
23394 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23395 if(!e.dom.checked){
23407 validateCheckbox : function()
23410 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23411 //return (this.getValue() == this.inputValue) ? true : false;
23414 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23422 for(var i in group){
23423 if(group[i].el.isVisible(true)){
23431 for(var i in group){
23436 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23443 * Mark this field as valid
23445 markValid : function()
23449 this.fireEvent('valid', this);
23451 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23454 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23461 if(this.inputType == 'radio'){
23462 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23463 var fg = e.findParent('.form-group', false, true);
23464 if (Roo.bootstrap.version == 3) {
23465 fg.removeClass([_this.invalidClass, _this.validClass]);
23466 fg.addClass(_this.validClass);
23468 fg.removeClass(['is-valid', 'is-invalid']);
23469 fg.addClass('is-valid');
23477 var fg = this.el.findParent('.form-group', false, true);
23478 if (Roo.bootstrap.version == 3) {
23479 fg.removeClass([this.invalidClass, this.validClass]);
23480 fg.addClass(this.validClass);
23482 fg.removeClass(['is-valid', 'is-invalid']);
23483 fg.addClass('is-valid');
23488 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23494 for(var i in group){
23495 var fg = group[i].el.findParent('.form-group', false, true);
23496 if (Roo.bootstrap.version == 3) {
23497 fg.removeClass([this.invalidClass, this.validClass]);
23498 fg.addClass(this.validClass);
23500 fg.removeClass(['is-valid', 'is-invalid']);
23501 fg.addClass('is-valid');
23507 * Mark this field as invalid
23508 * @param {String} msg The validation message
23510 markInvalid : function(msg)
23512 if(this.allowBlank){
23518 this.fireEvent('invalid', this, msg);
23520 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23523 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23527 label.markInvalid();
23530 if(this.inputType == 'radio'){
23532 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23533 var fg = e.findParent('.form-group', false, true);
23534 if (Roo.bootstrap.version == 3) {
23535 fg.removeClass([_this.invalidClass, _this.validClass]);
23536 fg.addClass(_this.invalidClass);
23538 fg.removeClass(['is-invalid', 'is-valid']);
23539 fg.addClass('is-invalid');
23547 var fg = this.el.findParent('.form-group', false, true);
23548 if (Roo.bootstrap.version == 3) {
23549 fg.removeClass([_this.invalidClass, _this.validClass]);
23550 fg.addClass(_this.invalidClass);
23552 fg.removeClass(['is-invalid', 'is-valid']);
23553 fg.addClass('is-invalid');
23558 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23564 for(var i in group){
23565 var fg = group[i].el.findParent('.form-group', false, true);
23566 if (Roo.bootstrap.version == 3) {
23567 fg.removeClass([_this.invalidClass, _this.validClass]);
23568 fg.addClass(_this.invalidClass);
23570 fg.removeClass(['is-invalid', 'is-valid']);
23571 fg.addClass('is-invalid');
23577 clearInvalid : function()
23579 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23581 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23583 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23585 if (label && label.iconEl) {
23586 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23587 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23591 disable : function()
23593 if(this.inputType != 'radio'){
23594 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23601 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23602 _this.getActionEl().addClass(this.disabledClass);
23603 e.dom.disabled = true;
23607 this.disabled = true;
23608 this.fireEvent("disable", this);
23612 enable : function()
23614 if(this.inputType != 'radio'){
23615 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23622 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23623 _this.getActionEl().removeClass(this.disabledClass);
23624 e.dom.disabled = false;
23628 this.disabled = false;
23629 this.fireEvent("enable", this);
23633 setBoxLabel : function(v)
23638 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23644 Roo.apply(Roo.bootstrap.CheckBox, {
23649 * register a CheckBox Group
23650 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23652 register : function(checkbox)
23654 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23655 this.groups[checkbox.groupId] = {};
23658 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23662 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23666 * fetch a CheckBox Group based on the group ID
23667 * @param {string} the group ID
23668 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23670 get: function(groupId) {
23671 if (typeof(this.groups[groupId]) == 'undefined') {
23675 return this.groups[groupId] ;
23688 * @class Roo.bootstrap.Radio
23689 * @extends Roo.bootstrap.Component
23690 * Bootstrap Radio class
23691 * @cfg {String} boxLabel - the label associated
23692 * @cfg {String} value - the value of radio
23695 * Create a new Radio
23696 * @param {Object} config The config object
23698 Roo.bootstrap.Radio = function(config){
23699 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23703 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23709 getAutoCreate : function()
23713 cls : 'form-group radio',
23718 html : this.boxLabel
23726 initEvents : function()
23728 this.parent().register(this);
23730 this.el.on('click', this.onClick, this);
23734 onClick : function(e)
23736 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23737 this.setChecked(true);
23741 setChecked : function(state, suppressEvent)
23743 this.parent().setValue(this.value, suppressEvent);
23747 setBoxLabel : function(v)
23752 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23767 * @class Roo.bootstrap.SecurePass
23768 * @extends Roo.bootstrap.Input
23769 * Bootstrap SecurePass class
23773 * Create a new SecurePass
23774 * @param {Object} config The config object
23777 Roo.bootstrap.SecurePass = function (config) {
23778 // these go here, so the translation tool can replace them..
23780 PwdEmpty: "Please type a password, and then retype it to confirm.",
23781 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23782 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23783 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23784 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23785 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23786 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23787 TooWeak: "Your password is Too Weak."
23789 this.meterLabel = "Password strength:";
23790 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23791 this.meterClass = [
23792 "roo-password-meter-tooweak",
23793 "roo-password-meter-weak",
23794 "roo-password-meter-medium",
23795 "roo-password-meter-strong",
23796 "roo-password-meter-grey"
23801 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23804 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23806 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23808 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23809 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23810 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23811 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23812 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23813 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23814 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23824 * @cfg {String/Object} Label for the strength meter (defaults to
23825 * 'Password strength:')
23830 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23831 * ['Weak', 'Medium', 'Strong'])
23834 pwdStrengths: false,
23847 initEvents: function ()
23849 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23851 if (this.el.is('input[type=password]') && Roo.isSafari) {
23852 this.el.on('keydown', this.SafariOnKeyDown, this);
23855 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23858 onRender: function (ct, position)
23860 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23861 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23862 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23864 this.trigger.createChild({
23869 cls: 'roo-password-meter-grey col-xs-12',
23872 //width: this.meterWidth + 'px'
23876 cls: 'roo-password-meter-text'
23882 if (this.hideTrigger) {
23883 this.trigger.setDisplayed(false);
23885 this.setSize(this.width || '', this.height || '');
23888 onDestroy: function ()
23890 if (this.trigger) {
23891 this.trigger.removeAllListeners();
23892 this.trigger.remove();
23895 this.wrap.remove();
23897 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23900 checkStrength: function ()
23902 var pwd = this.inputEl().getValue();
23903 if (pwd == this._lastPwd) {
23908 if (this.ClientSideStrongPassword(pwd)) {
23910 } else if (this.ClientSideMediumPassword(pwd)) {
23912 } else if (this.ClientSideWeakPassword(pwd)) {
23918 Roo.log('strength1: ' + strength);
23920 //var pm = this.trigger.child('div/div/div').dom;
23921 var pm = this.trigger.child('div/div');
23922 pm.removeClass(this.meterClass);
23923 pm.addClass(this.meterClass[strength]);
23926 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23928 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23930 this._lastPwd = pwd;
23934 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23936 this._lastPwd = '';
23938 var pm = this.trigger.child('div/div');
23939 pm.removeClass(this.meterClass);
23940 pm.addClass('roo-password-meter-grey');
23943 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23946 this.inputEl().dom.type='password';
23949 validateValue: function (value)
23951 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23954 if (value.length == 0) {
23955 if (this.allowBlank) {
23956 this.clearInvalid();
23960 this.markInvalid(this.errors.PwdEmpty);
23961 this.errorMsg = this.errors.PwdEmpty;
23969 if (!value.match(/[\x21-\x7e]+/)) {
23970 this.markInvalid(this.errors.PwdBadChar);
23971 this.errorMsg = this.errors.PwdBadChar;
23974 if (value.length < 6) {
23975 this.markInvalid(this.errors.PwdShort);
23976 this.errorMsg = this.errors.PwdShort;
23979 if (value.length > 16) {
23980 this.markInvalid(this.errors.PwdLong);
23981 this.errorMsg = this.errors.PwdLong;
23985 if (this.ClientSideStrongPassword(value)) {
23987 } else if (this.ClientSideMediumPassword(value)) {
23989 } else if (this.ClientSideWeakPassword(value)) {
23996 if (strength < 2) {
23997 //this.markInvalid(this.errors.TooWeak);
23998 this.errorMsg = this.errors.TooWeak;
24003 console.log('strength2: ' + strength);
24005 //var pm = this.trigger.child('div/div/div').dom;
24007 var pm = this.trigger.child('div/div');
24008 pm.removeClass(this.meterClass);
24009 pm.addClass(this.meterClass[strength]);
24011 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24013 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24015 this.errorMsg = '';
24019 CharacterSetChecks: function (type)
24022 this.fResult = false;
24025 isctype: function (character, type)
24028 case this.kCapitalLetter:
24029 if (character >= 'A' && character <= 'Z') {
24034 case this.kSmallLetter:
24035 if (character >= 'a' && character <= 'z') {
24041 if (character >= '0' && character <= '9') {
24046 case this.kPunctuation:
24047 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24058 IsLongEnough: function (pwd, size)
24060 return !(pwd == null || isNaN(size) || pwd.length < size);
24063 SpansEnoughCharacterSets: function (word, nb)
24065 if (!this.IsLongEnough(word, nb))
24070 var characterSetChecks = new Array(
24071 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24072 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24075 for (var index = 0; index < word.length; ++index) {
24076 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24077 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24078 characterSetChecks[nCharSet].fResult = true;
24085 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24086 if (characterSetChecks[nCharSet].fResult) {
24091 if (nCharSets < nb) {
24097 ClientSideStrongPassword: function (pwd)
24099 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24102 ClientSideMediumPassword: function (pwd)
24104 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24107 ClientSideWeakPassword: function (pwd)
24109 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24112 })//<script type="text/javascript">
24115 * Based Ext JS Library 1.1.1
24116 * Copyright(c) 2006-2007, Ext JS, LLC.
24122 * @class Roo.HtmlEditorCore
24123 * @extends Roo.Component
24124 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24126 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24129 Roo.HtmlEditorCore = function(config){
24132 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24137 * @event initialize
24138 * Fires when the editor is fully initialized (including the iframe)
24139 * @param {Roo.HtmlEditorCore} this
24144 * Fires when the editor is first receives the focus. Any insertion must wait
24145 * until after this event.
24146 * @param {Roo.HtmlEditorCore} this
24150 * @event beforesync
24151 * Fires before the textarea is updated with content from the editor iframe. Return false
24152 * to cancel the sync.
24153 * @param {Roo.HtmlEditorCore} this
24154 * @param {String} html
24158 * @event beforepush
24159 * Fires before the iframe editor is updated with content from the textarea. Return false
24160 * to cancel the push.
24161 * @param {Roo.HtmlEditorCore} this
24162 * @param {String} html
24167 * Fires when the textarea is updated with content from the editor iframe.
24168 * @param {Roo.HtmlEditorCore} this
24169 * @param {String} html
24174 * Fires when the iframe editor is updated with content from the textarea.
24175 * @param {Roo.HtmlEditorCore} this
24176 * @param {String} html
24181 * @event editorevent
24182 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24183 * @param {Roo.HtmlEditorCore} this
24189 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24191 // defaults : white / black...
24192 this.applyBlacklists();
24199 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24203 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24209 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24214 * @cfg {Number} height (in pixels)
24218 * @cfg {Number} width (in pixels)
24223 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24226 stylesheets: false,
24231 // private properties
24232 validationEvent : false,
24234 initialized : false,
24236 sourceEditMode : false,
24237 onFocus : Roo.emptyFn,
24239 hideMode:'offsets',
24243 // blacklist + whitelisted elements..
24250 * Protected method that will not generally be called directly. It
24251 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24252 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24254 getDocMarkup : function(){
24258 // inherit styels from page...??
24259 if (this.stylesheets === false) {
24261 Roo.get(document.head).select('style').each(function(node) {
24262 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24265 Roo.get(document.head).select('link').each(function(node) {
24266 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24269 } else if (!this.stylesheets.length) {
24271 st = '<style type="text/css">' +
24272 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24275 for (var i in this.stylesheets) {
24276 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24281 st += '<style type="text/css">' +
24282 'IMG { cursor: pointer } ' +
24285 var cls = 'roo-htmleditor-body';
24287 if(this.bodyCls.length){
24288 cls += ' ' + this.bodyCls;
24291 return '<html><head>' + st +
24292 //<style type="text/css">' +
24293 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24295 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24299 onRender : function(ct, position)
24302 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24303 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24306 this.el.dom.style.border = '0 none';
24307 this.el.dom.setAttribute('tabIndex', -1);
24308 this.el.addClass('x-hidden hide');
24312 if(Roo.isIE){ // fix IE 1px bogus margin
24313 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24317 this.frameId = Roo.id();
24321 var iframe = this.owner.wrap.createChild({
24323 cls: 'form-control', // bootstrap..
24325 name: this.frameId,
24326 frameBorder : 'no',
24327 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24332 this.iframe = iframe.dom;
24334 this.assignDocWin();
24336 this.doc.designMode = 'on';
24339 this.doc.write(this.getDocMarkup());
24343 var task = { // must defer to wait for browser to be ready
24345 //console.log("run task?" + this.doc.readyState);
24346 this.assignDocWin();
24347 if(this.doc.body || this.doc.readyState == 'complete'){
24349 this.doc.designMode="on";
24353 Roo.TaskMgr.stop(task);
24354 this.initEditor.defer(10, this);
24361 Roo.TaskMgr.start(task);
24366 onResize : function(w, h)
24368 Roo.log('resize: ' +w + ',' + h );
24369 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24373 if(typeof w == 'number'){
24375 this.iframe.style.width = w + 'px';
24377 if(typeof h == 'number'){
24379 this.iframe.style.height = h + 'px';
24381 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24388 * Toggles the editor between standard and source edit mode.
24389 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24391 toggleSourceEdit : function(sourceEditMode){
24393 this.sourceEditMode = sourceEditMode === true;
24395 if(this.sourceEditMode){
24397 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24400 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24401 //this.iframe.className = '';
24404 //this.setSize(this.owner.wrap.getSize());
24405 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24412 * Protected method that will not generally be called directly. If you need/want
24413 * custom HTML cleanup, this is the method you should override.
24414 * @param {String} html The HTML to be cleaned
24415 * return {String} The cleaned HTML
24417 cleanHtml : function(html){
24418 html = String(html);
24419 if(html.length > 5){
24420 if(Roo.isSafari){ // strip safari nonsense
24421 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24424 if(html == ' '){
24431 * HTML Editor -> Textarea
24432 * Protected method that will not generally be called directly. Syncs the contents
24433 * of the editor iframe with the textarea.
24435 syncValue : function(){
24436 if(this.initialized){
24437 var bd = (this.doc.body || this.doc.documentElement);
24438 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24439 var html = bd.innerHTML;
24441 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24442 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24444 html = '<div style="'+m[0]+'">' + html + '</div>';
24447 html = this.cleanHtml(html);
24448 // fix up the special chars.. normaly like back quotes in word...
24449 // however we do not want to do this with chinese..
24450 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24452 var cc = match.charCodeAt();
24454 // Get the character value, handling surrogate pairs
24455 if (match.length == 2) {
24456 // It's a surrogate pair, calculate the Unicode code point
24457 var high = match.charCodeAt(0) - 0xD800;
24458 var low = match.charCodeAt(1) - 0xDC00;
24459 cc = (high * 0x400) + low + 0x10000;
24461 (cc >= 0x4E00 && cc < 0xA000 ) ||
24462 (cc >= 0x3400 && cc < 0x4E00 ) ||
24463 (cc >= 0xf900 && cc < 0xfb00 )
24468 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24469 return "&#" + cc + ";";
24476 if(this.owner.fireEvent('beforesync', this, html) !== false){
24477 this.el.dom.value = html;
24478 this.owner.fireEvent('sync', this, html);
24484 * Protected method that will not generally be called directly. Pushes the value of the textarea
24485 * into the iframe editor.
24487 pushValue : function(){
24488 if(this.initialized){
24489 var v = this.el.dom.value.trim();
24491 // if(v.length < 1){
24495 if(this.owner.fireEvent('beforepush', this, v) !== false){
24496 var d = (this.doc.body || this.doc.documentElement);
24498 this.cleanUpPaste();
24499 this.el.dom.value = d.innerHTML;
24500 this.owner.fireEvent('push', this, v);
24506 deferFocus : function(){
24507 this.focus.defer(10, this);
24511 focus : function(){
24512 if(this.win && !this.sourceEditMode){
24519 assignDocWin: function()
24521 var iframe = this.iframe;
24524 this.doc = iframe.contentWindow.document;
24525 this.win = iframe.contentWindow;
24527 // if (!Roo.get(this.frameId)) {
24530 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24531 // this.win = Roo.get(this.frameId).dom.contentWindow;
24533 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24537 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24538 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24543 initEditor : function(){
24544 //console.log("INIT EDITOR");
24545 this.assignDocWin();
24549 this.doc.designMode="on";
24551 this.doc.write(this.getDocMarkup());
24554 var dbody = (this.doc.body || this.doc.documentElement);
24555 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24556 // this copies styles from the containing element into thsi one..
24557 // not sure why we need all of this..
24558 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24560 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24561 //ss['background-attachment'] = 'fixed'; // w3c
24562 dbody.bgProperties = 'fixed'; // ie
24563 //Roo.DomHelper.applyStyles(dbody, ss);
24564 Roo.EventManager.on(this.doc, {
24565 //'mousedown': this.onEditorEvent,
24566 'mouseup': this.onEditorEvent,
24567 'dblclick': this.onEditorEvent,
24568 'click': this.onEditorEvent,
24569 'keyup': this.onEditorEvent,
24574 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24576 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24577 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24579 this.initialized = true;
24581 this.owner.fireEvent('initialize', this);
24586 onDestroy : function(){
24592 //for (var i =0; i < this.toolbars.length;i++) {
24593 // // fixme - ask toolbars for heights?
24594 // this.toolbars[i].onDestroy();
24597 //this.wrap.dom.innerHTML = '';
24598 //this.wrap.remove();
24603 onFirstFocus : function(){
24605 this.assignDocWin();
24608 this.activated = true;
24611 if(Roo.isGecko){ // prevent silly gecko errors
24613 var s = this.win.getSelection();
24614 if(!s.focusNode || s.focusNode.nodeType != 3){
24615 var r = s.getRangeAt(0);
24616 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24621 this.execCmd('useCSS', true);
24622 this.execCmd('styleWithCSS', false);
24625 this.owner.fireEvent('activate', this);
24629 adjustFont: function(btn){
24630 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24631 //if(Roo.isSafari){ // safari
24634 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24635 if(Roo.isSafari){ // safari
24636 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24637 v = (v < 10) ? 10 : v;
24638 v = (v > 48) ? 48 : v;
24639 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24644 v = Math.max(1, v+adjust);
24646 this.execCmd('FontSize', v );
24649 onEditorEvent : function(e)
24651 this.owner.fireEvent('editorevent', this, e);
24652 // this.updateToolbar();
24653 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24656 insertTag : function(tg)
24658 // could be a bit smarter... -> wrap the current selected tRoo..
24659 if (tg.toLowerCase() == 'span' ||
24660 tg.toLowerCase() == 'code' ||
24661 tg.toLowerCase() == 'sup' ||
24662 tg.toLowerCase() == 'sub'
24665 range = this.createRange(this.getSelection());
24666 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24667 wrappingNode.appendChild(range.extractContents());
24668 range.insertNode(wrappingNode);
24675 this.execCmd("formatblock", tg);
24679 insertText : function(txt)
24683 var range = this.createRange();
24684 range.deleteContents();
24685 //alert(Sender.getAttribute('label'));
24687 range.insertNode(this.doc.createTextNode(txt));
24693 * Executes a Midas editor command on the editor document and performs necessary focus and
24694 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24695 * @param {String} cmd The Midas command
24696 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24698 relayCmd : function(cmd, value){
24700 this.execCmd(cmd, value);
24701 this.owner.fireEvent('editorevent', this);
24702 //this.updateToolbar();
24703 this.owner.deferFocus();
24707 * Executes a Midas editor command directly on the editor document.
24708 * For visual commands, you should use {@link #relayCmd} instead.
24709 * <b>This should only be called after the editor is initialized.</b>
24710 * @param {String} cmd The Midas command
24711 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24713 execCmd : function(cmd, value){
24714 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24721 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24723 * @param {String} text | dom node..
24725 insertAtCursor : function(text)
24728 if(!this.activated){
24734 var r = this.doc.selection.createRange();
24745 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24749 // from jquery ui (MIT licenced)
24751 var win = this.win;
24753 if (win.getSelection && win.getSelection().getRangeAt) {
24754 range = win.getSelection().getRangeAt(0);
24755 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24756 range.insertNode(node);
24757 } else if (win.document.selection && win.document.selection.createRange) {
24758 // no firefox support
24759 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24760 win.document.selection.createRange().pasteHTML(txt);
24762 // no firefox support
24763 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24764 this.execCmd('InsertHTML', txt);
24773 mozKeyPress : function(e){
24775 var c = e.getCharCode(), cmd;
24778 c = String.fromCharCode(c).toLowerCase();
24792 this.cleanUpPaste.defer(100, this);
24800 e.preventDefault();
24808 fixKeys : function(){ // load time branching for fastest keydown performance
24810 return function(e){
24811 var k = e.getKey(), r;
24814 r = this.doc.selection.createRange();
24817 r.pasteHTML('    ');
24824 r = this.doc.selection.createRange();
24826 var target = r.parentElement();
24827 if(!target || target.tagName.toLowerCase() != 'li'){
24829 r.pasteHTML('<br />');
24835 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24836 this.cleanUpPaste.defer(100, this);
24842 }else if(Roo.isOpera){
24843 return function(e){
24844 var k = e.getKey();
24848 this.execCmd('InsertHTML','    ');
24851 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24852 this.cleanUpPaste.defer(100, this);
24857 }else if(Roo.isSafari){
24858 return function(e){
24859 var k = e.getKey();
24863 this.execCmd('InsertText','\t');
24867 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24868 this.cleanUpPaste.defer(100, this);
24876 getAllAncestors: function()
24878 var p = this.getSelectedNode();
24881 a.push(p); // push blank onto stack..
24882 p = this.getParentElement();
24886 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24890 a.push(this.doc.body);
24894 lastSelNode : false,
24897 getSelection : function()
24899 this.assignDocWin();
24900 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24903 getSelectedNode: function()
24905 // this may only work on Gecko!!!
24907 // should we cache this!!!!
24912 var range = this.createRange(this.getSelection()).cloneRange();
24915 var parent = range.parentElement();
24917 var testRange = range.duplicate();
24918 testRange.moveToElementText(parent);
24919 if (testRange.inRange(range)) {
24922 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24925 parent = parent.parentElement;
24930 // is ancestor a text element.
24931 var ac = range.commonAncestorContainer;
24932 if (ac.nodeType == 3) {
24933 ac = ac.parentNode;
24936 var ar = ac.childNodes;
24939 var other_nodes = [];
24940 var has_other_nodes = false;
24941 for (var i=0;i<ar.length;i++) {
24942 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24945 // fullly contained node.
24947 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24952 // probably selected..
24953 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24954 other_nodes.push(ar[i]);
24958 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24963 has_other_nodes = true;
24965 if (!nodes.length && other_nodes.length) {
24966 nodes= other_nodes;
24968 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24974 createRange: function(sel)
24976 // this has strange effects when using with
24977 // top toolbar - not sure if it's a great idea.
24978 //this.editor.contentWindow.focus();
24979 if (typeof sel != "undefined") {
24981 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24983 return this.doc.createRange();
24986 return this.doc.createRange();
24989 getParentElement: function()
24992 this.assignDocWin();
24993 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24995 var range = this.createRange(sel);
24998 var p = range.commonAncestorContainer;
24999 while (p.nodeType == 3) { // text node
25010 * Range intersection.. the hard stuff...
25014 * [ -- selected range --- ]
25018 * if end is before start or hits it. fail.
25019 * if start is after end or hits it fail.
25021 * if either hits (but other is outside. - then it's not
25027 // @see http://www.thismuchiknow.co.uk/?p=64.
25028 rangeIntersectsNode : function(range, node)
25030 var nodeRange = node.ownerDocument.createRange();
25032 nodeRange.selectNode(node);
25034 nodeRange.selectNodeContents(node);
25037 var rangeStartRange = range.cloneRange();
25038 rangeStartRange.collapse(true);
25040 var rangeEndRange = range.cloneRange();
25041 rangeEndRange.collapse(false);
25043 var nodeStartRange = nodeRange.cloneRange();
25044 nodeStartRange.collapse(true);
25046 var nodeEndRange = nodeRange.cloneRange();
25047 nodeEndRange.collapse(false);
25049 return rangeStartRange.compareBoundaryPoints(
25050 Range.START_TO_START, nodeEndRange) == -1 &&
25051 rangeEndRange.compareBoundaryPoints(
25052 Range.START_TO_START, nodeStartRange) == 1;
25056 rangeCompareNode : function(range, node)
25058 var nodeRange = node.ownerDocument.createRange();
25060 nodeRange.selectNode(node);
25062 nodeRange.selectNodeContents(node);
25066 range.collapse(true);
25068 nodeRange.collapse(true);
25070 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25071 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25073 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25075 var nodeIsBefore = ss == 1;
25076 var nodeIsAfter = ee == -1;
25078 if (nodeIsBefore && nodeIsAfter) {
25081 if (!nodeIsBefore && nodeIsAfter) {
25082 return 1; //right trailed.
25085 if (nodeIsBefore && !nodeIsAfter) {
25086 return 2; // left trailed.
25092 // private? - in a new class?
25093 cleanUpPaste : function()
25095 // cleans up the whole document..
25096 Roo.log('cleanuppaste');
25098 this.cleanUpChildren(this.doc.body);
25099 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25100 if (clean != this.doc.body.innerHTML) {
25101 this.doc.body.innerHTML = clean;
25106 cleanWordChars : function(input) {// change the chars to hex code
25107 var he = Roo.HtmlEditorCore;
25109 var output = input;
25110 Roo.each(he.swapCodes, function(sw) {
25111 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25113 output = output.replace(swapper, sw[1]);
25120 cleanUpChildren : function (n)
25122 if (!n.childNodes.length) {
25125 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25126 this.cleanUpChild(n.childNodes[i]);
25133 cleanUpChild : function (node)
25136 //console.log(node);
25137 if (node.nodeName == "#text") {
25138 // clean up silly Windows -- stuff?
25141 if (node.nodeName == "#comment") {
25142 node.parentNode.removeChild(node);
25143 // clean up silly Windows -- stuff?
25146 var lcname = node.tagName.toLowerCase();
25147 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25148 // whitelist of tags..
25150 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25152 node.parentNode.removeChild(node);
25157 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25159 // spans with no attributes - just remove them..
25160 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25161 remove_keep_children = true;
25164 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25165 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25167 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25168 // remove_keep_children = true;
25171 if (remove_keep_children) {
25172 this.cleanUpChildren(node);
25173 // inserts everything just before this node...
25174 while (node.childNodes.length) {
25175 var cn = node.childNodes[0];
25176 node.removeChild(cn);
25177 node.parentNode.insertBefore(cn, node);
25179 node.parentNode.removeChild(node);
25183 if (!node.attributes || !node.attributes.length) {
25188 this.cleanUpChildren(node);
25192 function cleanAttr(n,v)
25195 if (v.match(/^\./) || v.match(/^\//)) {
25198 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25201 if (v.match(/^#/)) {
25204 if (v.match(/^\{/)) { // allow template editing.
25207 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25208 node.removeAttribute(n);
25212 var cwhite = this.cwhite;
25213 var cblack = this.cblack;
25215 function cleanStyle(n,v)
25217 if (v.match(/expression/)) { //XSS?? should we even bother..
25218 node.removeAttribute(n);
25222 var parts = v.split(/;/);
25225 Roo.each(parts, function(p) {
25226 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25230 var l = p.split(':').shift().replace(/\s+/g,'');
25231 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25233 if ( cwhite.length && cblack.indexOf(l) > -1) {
25234 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25235 //node.removeAttribute(n);
25239 // only allow 'c whitelisted system attributes'
25240 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25241 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25242 //node.removeAttribute(n);
25252 if (clean.length) {
25253 node.setAttribute(n, clean.join(';'));
25255 node.removeAttribute(n);
25261 for (var i = node.attributes.length-1; i > -1 ; i--) {
25262 var a = node.attributes[i];
25265 if (a.name.toLowerCase().substr(0,2)=='on') {
25266 node.removeAttribute(a.name);
25269 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25270 node.removeAttribute(a.name);
25273 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25274 cleanAttr(a.name,a.value); // fixme..
25277 if (a.name == 'style') {
25278 cleanStyle(a.name,a.value);
25281 /// clean up MS crap..
25282 // tecnically this should be a list of valid class'es..
25285 if (a.name == 'class') {
25286 if (a.value.match(/^Mso/)) {
25287 node.removeAttribute('class');
25290 if (a.value.match(/^body$/)) {
25291 node.removeAttribute('class');
25302 this.cleanUpChildren(node);
25308 * Clean up MS wordisms...
25310 cleanWord : function(node)
25313 this.cleanWord(this.doc.body);
25318 node.nodeName == 'SPAN' &&
25319 !node.hasAttributes() &&
25320 node.childNodes.length == 1 &&
25321 node.firstChild.nodeName == "#text"
25323 var textNode = node.firstChild;
25324 node.removeChild(textNode);
25325 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25326 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25328 node.parentNode.insertBefore(textNode, node);
25329 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25330 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25332 node.parentNode.removeChild(node);
25335 if (node.nodeName == "#text") {
25336 // clean up silly Windows -- stuff?
25339 if (node.nodeName == "#comment") {
25340 node.parentNode.removeChild(node);
25341 // clean up silly Windows -- stuff?
25345 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25346 node.parentNode.removeChild(node);
25349 //Roo.log(node.tagName);
25350 // remove - but keep children..
25351 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25352 //Roo.log('-- removed');
25353 while (node.childNodes.length) {
25354 var cn = node.childNodes[0];
25355 node.removeChild(cn);
25356 node.parentNode.insertBefore(cn, node);
25357 // move node to parent - and clean it..
25358 this.cleanWord(cn);
25360 node.parentNode.removeChild(node);
25361 /// no need to iterate chidlren = it's got none..
25362 //this.iterateChildren(node, this.cleanWord);
25366 if (node.className.length) {
25368 var cn = node.className.split(/\W+/);
25370 Roo.each(cn, function(cls) {
25371 if (cls.match(/Mso[a-zA-Z]+/)) {
25376 node.className = cna.length ? cna.join(' ') : '';
25378 node.removeAttribute("class");
25382 if (node.hasAttribute("lang")) {
25383 node.removeAttribute("lang");
25386 if (node.hasAttribute("style")) {
25388 var styles = node.getAttribute("style").split(";");
25390 Roo.each(styles, function(s) {
25391 if (!s.match(/:/)) {
25394 var kv = s.split(":");
25395 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25398 // what ever is left... we allow.
25401 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25402 if (!nstyle.length) {
25403 node.removeAttribute('style');
25406 this.iterateChildren(node, this.cleanWord);
25412 * iterateChildren of a Node, calling fn each time, using this as the scole..
25413 * @param {DomNode} node node to iterate children of.
25414 * @param {Function} fn method of this class to call on each item.
25416 iterateChildren : function(node, fn)
25418 if (!node.childNodes.length) {
25421 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25422 fn.call(this, node.childNodes[i])
25428 * cleanTableWidths.
25430 * Quite often pasting from word etc.. results in tables with column and widths.
25431 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25434 cleanTableWidths : function(node)
25439 this.cleanTableWidths(this.doc.body);
25444 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25447 Roo.log(node.tagName);
25448 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25449 this.iterateChildren(node, this.cleanTableWidths);
25452 if (node.hasAttribute('width')) {
25453 node.removeAttribute('width');
25457 if (node.hasAttribute("style")) {
25460 var styles = node.getAttribute("style").split(";");
25462 Roo.each(styles, function(s) {
25463 if (!s.match(/:/)) {
25466 var kv = s.split(":");
25467 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25470 // what ever is left... we allow.
25473 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25474 if (!nstyle.length) {
25475 node.removeAttribute('style');
25479 this.iterateChildren(node, this.cleanTableWidths);
25487 domToHTML : function(currentElement, depth, nopadtext) {
25489 depth = depth || 0;
25490 nopadtext = nopadtext || false;
25492 if (!currentElement) {
25493 return this.domToHTML(this.doc.body);
25496 //Roo.log(currentElement);
25498 var allText = false;
25499 var nodeName = currentElement.nodeName;
25500 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25502 if (nodeName == '#text') {
25504 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25509 if (nodeName != 'BODY') {
25512 // Prints the node tagName, such as <A>, <IMG>, etc
25515 for(i = 0; i < currentElement.attributes.length;i++) {
25517 var aname = currentElement.attributes.item(i).name;
25518 if (!currentElement.attributes.item(i).value.length) {
25521 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25524 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25533 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25536 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25541 // Traverse the tree
25543 var currentElementChild = currentElement.childNodes.item(i);
25544 var allText = true;
25545 var innerHTML = '';
25547 while (currentElementChild) {
25548 // Formatting code (indent the tree so it looks nice on the screen)
25549 var nopad = nopadtext;
25550 if (lastnode == 'SPAN') {
25554 if (currentElementChild.nodeName == '#text') {
25555 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25556 toadd = nopadtext ? toadd : toadd.trim();
25557 if (!nopad && toadd.length > 80) {
25558 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25560 innerHTML += toadd;
25563 currentElementChild = currentElement.childNodes.item(i);
25569 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25571 // Recursively traverse the tree structure of the child node
25572 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25573 lastnode = currentElementChild.nodeName;
25575 currentElementChild=currentElement.childNodes.item(i);
25581 // The remaining code is mostly for formatting the tree
25582 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25587 ret+= "</"+tagName+">";
25593 applyBlacklists : function()
25595 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25596 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25600 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25601 if (b.indexOf(tag) > -1) {
25604 this.white.push(tag);
25608 Roo.each(w, function(tag) {
25609 if (b.indexOf(tag) > -1) {
25612 if (this.white.indexOf(tag) > -1) {
25615 this.white.push(tag);
25620 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25621 if (w.indexOf(tag) > -1) {
25624 this.black.push(tag);
25628 Roo.each(b, function(tag) {
25629 if (w.indexOf(tag) > -1) {
25632 if (this.black.indexOf(tag) > -1) {
25635 this.black.push(tag);
25640 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25641 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25645 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25646 if (b.indexOf(tag) > -1) {
25649 this.cwhite.push(tag);
25653 Roo.each(w, function(tag) {
25654 if (b.indexOf(tag) > -1) {
25657 if (this.cwhite.indexOf(tag) > -1) {
25660 this.cwhite.push(tag);
25665 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25666 if (w.indexOf(tag) > -1) {
25669 this.cblack.push(tag);
25673 Roo.each(b, function(tag) {
25674 if (w.indexOf(tag) > -1) {
25677 if (this.cblack.indexOf(tag) > -1) {
25680 this.cblack.push(tag);
25685 setStylesheets : function(stylesheets)
25687 if(typeof(stylesheets) == 'string'){
25688 Roo.get(this.iframe.contentDocument.head).createChild({
25690 rel : 'stylesheet',
25699 Roo.each(stylesheets, function(s) {
25704 Roo.get(_this.iframe.contentDocument.head).createChild({
25706 rel : 'stylesheet',
25715 removeStylesheets : function()
25719 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25724 setStyle : function(style)
25726 Roo.get(this.iframe.contentDocument.head).createChild({
25735 // hide stuff that is not compatible
25749 * @event specialkey
25753 * @cfg {String} fieldClass @hide
25756 * @cfg {String} focusClass @hide
25759 * @cfg {String} autoCreate @hide
25762 * @cfg {String} inputType @hide
25765 * @cfg {String} invalidClass @hide
25768 * @cfg {String} invalidText @hide
25771 * @cfg {String} msgFx @hide
25774 * @cfg {String} validateOnBlur @hide
25778 Roo.HtmlEditorCore.white = [
25779 'area', 'br', 'img', 'input', 'hr', 'wbr',
25781 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25782 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25783 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25784 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25785 'table', 'ul', 'xmp',
25787 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25790 'dir', 'menu', 'ol', 'ul', 'dl',
25796 Roo.HtmlEditorCore.black = [
25797 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25799 'base', 'basefont', 'bgsound', 'blink', 'body',
25800 'frame', 'frameset', 'head', 'html', 'ilayer',
25801 'iframe', 'layer', 'link', 'meta', 'object',
25802 'script', 'style' ,'title', 'xml' // clean later..
25804 Roo.HtmlEditorCore.clean = [
25805 'script', 'style', 'title', 'xml'
25807 Roo.HtmlEditorCore.remove = [
25812 Roo.HtmlEditorCore.ablack = [
25816 Roo.HtmlEditorCore.aclean = [
25817 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25821 Roo.HtmlEditorCore.pwhite= [
25822 'http', 'https', 'mailto'
25825 // white listed style attributes.
25826 Roo.HtmlEditorCore.cwhite= [
25827 // 'text-align', /// default is to allow most things..
25833 // black listed style attributes.
25834 Roo.HtmlEditorCore.cblack= [
25835 // 'font-size' -- this can be set by the project
25839 Roo.HtmlEditorCore.swapCodes =[
25840 [ 8211, "–" ],
25841 [ 8212, "—" ],
25858 * @class Roo.bootstrap.HtmlEditor
25859 * @extends Roo.bootstrap.TextArea
25860 * Bootstrap HtmlEditor class
25863 * Create a new HtmlEditor
25864 * @param {Object} config The config object
25867 Roo.bootstrap.HtmlEditor = function(config){
25868 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25869 if (!this.toolbars) {
25870 this.toolbars = [];
25873 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25876 * @event initialize
25877 * Fires when the editor is fully initialized (including the iframe)
25878 * @param {HtmlEditor} this
25883 * Fires when the editor is first receives the focus. Any insertion must wait
25884 * until after this event.
25885 * @param {HtmlEditor} this
25889 * @event beforesync
25890 * Fires before the textarea is updated with content from the editor iframe. Return false
25891 * to cancel the sync.
25892 * @param {HtmlEditor} this
25893 * @param {String} html
25897 * @event beforepush
25898 * Fires before the iframe editor is updated with content from the textarea. Return false
25899 * to cancel the push.
25900 * @param {HtmlEditor} this
25901 * @param {String} html
25906 * Fires when the textarea is updated with content from the editor iframe.
25907 * @param {HtmlEditor} this
25908 * @param {String} html
25913 * Fires when the iframe editor is updated with content from the textarea.
25914 * @param {HtmlEditor} this
25915 * @param {String} html
25919 * @event editmodechange
25920 * Fires when the editor switches edit modes
25921 * @param {HtmlEditor} this
25922 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25924 editmodechange: true,
25926 * @event editorevent
25927 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25928 * @param {HtmlEditor} this
25932 * @event firstfocus
25933 * Fires when on first focus - needed by toolbars..
25934 * @param {HtmlEditor} this
25939 * Auto save the htmlEditor value as a file into Events
25940 * @param {HtmlEditor} this
25944 * @event savedpreview
25945 * preview the saved version of htmlEditor
25946 * @param {HtmlEditor} this
25953 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25957 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25962 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25967 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25972 * @cfg {Number} height (in pixels)
25976 * @cfg {Number} width (in pixels)
25981 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25984 stylesheets: false,
25989 // private properties
25990 validationEvent : false,
25992 initialized : false,
25995 onFocus : Roo.emptyFn,
25997 hideMode:'offsets',
25999 tbContainer : false,
26003 toolbarContainer :function() {
26004 return this.wrap.select('.x-html-editor-tb',true).first();
26008 * Protected method that will not generally be called directly. It
26009 * is called when the editor creates its toolbar. Override this method if you need to
26010 * add custom toolbar buttons.
26011 * @param {HtmlEditor} editor
26013 createToolbar : function(){
26014 Roo.log('renewing');
26015 Roo.log("create toolbars");
26017 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26018 this.toolbars[0].render(this.toolbarContainer());
26022 // if (!editor.toolbars || !editor.toolbars.length) {
26023 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26026 // for (var i =0 ; i < editor.toolbars.length;i++) {
26027 // editor.toolbars[i] = Roo.factory(
26028 // typeof(editor.toolbars[i]) == 'string' ?
26029 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26030 // Roo.bootstrap.HtmlEditor);
26031 // editor.toolbars[i].init(editor);
26037 onRender : function(ct, position)
26039 // Roo.log("Call onRender: " + this.xtype);
26041 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26043 this.wrap = this.inputEl().wrap({
26044 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26047 this.editorcore.onRender(ct, position);
26049 if (this.resizable) {
26050 this.resizeEl = new Roo.Resizable(this.wrap, {
26054 minHeight : this.height,
26055 height: this.height,
26056 handles : this.resizable,
26059 resize : function(r, w, h) {
26060 _t.onResize(w,h); // -something
26066 this.createToolbar(this);
26069 if(!this.width && this.resizable){
26070 this.setSize(this.wrap.getSize());
26072 if (this.resizeEl) {
26073 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26074 // should trigger onReize..
26080 onResize : function(w, h)
26082 Roo.log('resize: ' +w + ',' + h );
26083 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26087 if(this.inputEl() ){
26088 if(typeof w == 'number'){
26089 var aw = w - this.wrap.getFrameWidth('lr');
26090 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26093 if(typeof h == 'number'){
26094 var tbh = -11; // fixme it needs to tool bar size!
26095 for (var i =0; i < this.toolbars.length;i++) {
26096 // fixme - ask toolbars for heights?
26097 tbh += this.toolbars[i].el.getHeight();
26098 //if (this.toolbars[i].footer) {
26099 // tbh += this.toolbars[i].footer.el.getHeight();
26107 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26108 ah -= 5; // knock a few pixes off for look..
26109 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26113 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26114 this.editorcore.onResize(ew,eh);
26119 * Toggles the editor between standard and source edit mode.
26120 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26122 toggleSourceEdit : function(sourceEditMode)
26124 this.editorcore.toggleSourceEdit(sourceEditMode);
26126 if(this.editorcore.sourceEditMode){
26127 Roo.log('editor - showing textarea');
26130 // Roo.log(this.syncValue());
26132 this.inputEl().removeClass(['hide', 'x-hidden']);
26133 this.inputEl().dom.removeAttribute('tabIndex');
26134 this.inputEl().focus();
26136 Roo.log('editor - hiding textarea');
26138 // Roo.log(this.pushValue());
26141 this.inputEl().addClass(['hide', 'x-hidden']);
26142 this.inputEl().dom.setAttribute('tabIndex', -1);
26143 //this.deferFocus();
26146 if(this.resizable){
26147 this.setSize(this.wrap.getSize());
26150 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26153 // private (for BoxComponent)
26154 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26156 // private (for BoxComponent)
26157 getResizeEl : function(){
26161 // private (for BoxComponent)
26162 getPositionEl : function(){
26167 initEvents : function(){
26168 this.originalValue = this.getValue();
26172 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26175 // markInvalid : Roo.emptyFn,
26177 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26180 // clearInvalid : Roo.emptyFn,
26182 setValue : function(v){
26183 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26184 this.editorcore.pushValue();
26189 deferFocus : function(){
26190 this.focus.defer(10, this);
26194 focus : function(){
26195 this.editorcore.focus();
26201 onDestroy : function(){
26207 for (var i =0; i < this.toolbars.length;i++) {
26208 // fixme - ask toolbars for heights?
26209 this.toolbars[i].onDestroy();
26212 this.wrap.dom.innerHTML = '';
26213 this.wrap.remove();
26218 onFirstFocus : function(){
26219 //Roo.log("onFirstFocus");
26220 this.editorcore.onFirstFocus();
26221 for (var i =0; i < this.toolbars.length;i++) {
26222 this.toolbars[i].onFirstFocus();
26228 syncValue : function()
26230 this.editorcore.syncValue();
26233 pushValue : function()
26235 this.editorcore.pushValue();
26239 // hide stuff that is not compatible
26253 * @event specialkey
26257 * @cfg {String} fieldClass @hide
26260 * @cfg {String} focusClass @hide
26263 * @cfg {String} autoCreate @hide
26266 * @cfg {String} inputType @hide
26270 * @cfg {String} invalidText @hide
26273 * @cfg {String} msgFx @hide
26276 * @cfg {String} validateOnBlur @hide
26285 Roo.namespace('Roo.bootstrap.htmleditor');
26287 * @class Roo.bootstrap.HtmlEditorToolbar1
26293 new Roo.bootstrap.HtmlEditor({
26296 new Roo.bootstrap.HtmlEditorToolbar1({
26297 disable : { fonts: 1 , format: 1, ..., ... , ...],
26303 * @cfg {Object} disable List of elements to disable..
26304 * @cfg {Array} btns List of additional buttons.
26308 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26311 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26314 Roo.apply(this, config);
26316 // default disabled, based on 'good practice'..
26317 this.disable = this.disable || {};
26318 Roo.applyIf(this.disable, {
26321 specialElements : true
26323 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26325 this.editor = config.editor;
26326 this.editorcore = config.editor.editorcore;
26328 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26330 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26331 // dont call parent... till later.
26333 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26338 editorcore : false,
26343 "h1","h2","h3","h4","h5","h6",
26345 "abbr", "acronym", "address", "cite", "samp", "var",
26349 onRender : function(ct, position)
26351 // Roo.log("Call onRender: " + this.xtype);
26353 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26355 this.el.dom.style.marginBottom = '0';
26357 var editorcore = this.editorcore;
26358 var editor= this.editor;
26361 var btn = function(id,cmd , toggle, handler, html){
26363 var event = toggle ? 'toggle' : 'click';
26368 xns: Roo.bootstrap,
26372 enableToggle:toggle !== false,
26374 pressed : toggle ? false : null,
26377 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26378 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26384 // var cb_box = function...
26389 xns: Roo.bootstrap,
26394 xns: Roo.bootstrap,
26398 Roo.each(this.formats, function(f) {
26399 style.menu.items.push({
26401 xns: Roo.bootstrap,
26402 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26407 editorcore.insertTag(this.tagname);
26414 children.push(style);
26416 btn('bold',false,true);
26417 btn('italic',false,true);
26418 btn('align-left', 'justifyleft',true);
26419 btn('align-center', 'justifycenter',true);
26420 btn('align-right' , 'justifyright',true);
26421 btn('link', false, false, function(btn) {
26422 //Roo.log("create link?");
26423 var url = prompt(this.createLinkText, this.defaultLinkValue);
26424 if(url && url != 'http:/'+'/'){
26425 this.editorcore.relayCmd('createlink', url);
26428 btn('list','insertunorderedlist',true);
26429 btn('pencil', false,true, function(btn){
26431 this.toggleSourceEdit(btn.pressed);
26434 if (this.editor.btns.length > 0) {
26435 for (var i = 0; i<this.editor.btns.length; i++) {
26436 children.push(this.editor.btns[i]);
26444 xns: Roo.bootstrap,
26449 xns: Roo.bootstrap,
26454 cog.menu.items.push({
26456 xns: Roo.bootstrap,
26457 html : Clean styles,
26462 editorcore.insertTag(this.tagname);
26471 this.xtype = 'NavSimplebar';
26473 for(var i=0;i< children.length;i++) {
26475 this.buttons.add(this.addxtypeChild(children[i]));
26479 editor.on('editorevent', this.updateToolbar, this);
26481 onBtnClick : function(id)
26483 this.editorcore.relayCmd(id);
26484 this.editorcore.focus();
26488 * Protected method that will not generally be called directly. It triggers
26489 * a toolbar update by reading the markup state of the current selection in the editor.
26491 updateToolbar: function(){
26493 if(!this.editorcore.activated){
26494 this.editor.onFirstFocus(); // is this neeed?
26498 var btns = this.buttons;
26499 var doc = this.editorcore.doc;
26500 btns.get('bold').setActive(doc.queryCommandState('bold'));
26501 btns.get('italic').setActive(doc.queryCommandState('italic'));
26502 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26504 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26505 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26506 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26508 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26509 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26512 var ans = this.editorcore.getAllAncestors();
26513 if (this.formatCombo) {
26516 var store = this.formatCombo.store;
26517 this.formatCombo.setValue("");
26518 for (var i =0; i < ans.length;i++) {
26519 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26521 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26529 // hides menus... - so this cant be on a menu...
26530 Roo.bootstrap.MenuMgr.hideAll();
26532 Roo.bootstrap.MenuMgr.hideAll();
26533 //this.editorsyncValue();
26535 onFirstFocus: function() {
26536 this.buttons.each(function(item){
26540 toggleSourceEdit : function(sourceEditMode){
26543 if(sourceEditMode){
26544 Roo.log("disabling buttons");
26545 this.buttons.each( function(item){
26546 if(item.cmd != 'pencil'){
26552 Roo.log("enabling buttons");
26553 if(this.editorcore.initialized){
26554 this.buttons.each( function(item){
26560 Roo.log("calling toggole on editor");
26561 // tell the editor that it's been pressed..
26562 this.editor.toggleSourceEdit(sourceEditMode);
26576 * @class Roo.bootstrap.Markdown
26577 * @extends Roo.bootstrap.TextArea
26578 * Bootstrap Showdown editable area
26579 * @cfg {string} content
26582 * Create a new Showdown
26585 Roo.bootstrap.Markdown = function(config){
26586 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26590 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26594 initEvents : function()
26597 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26598 this.markdownEl = this.el.createChild({
26599 cls : 'roo-markdown-area'
26601 this.inputEl().addClass('d-none');
26602 if (this.getValue() == '') {
26603 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26606 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26608 this.markdownEl.on('click', this.toggleTextEdit, this);
26609 this.on('blur', this.toggleTextEdit, this);
26610 this.on('specialkey', this.resizeTextArea, this);
26613 toggleTextEdit : function()
26615 var sh = this.markdownEl.getHeight();
26616 this.inputEl().addClass('d-none');
26617 this.markdownEl.addClass('d-none');
26618 if (!this.editing) {
26620 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26621 this.inputEl().removeClass('d-none');
26622 this.inputEl().focus();
26623 this.editing = true;
26626 // show showdown...
26627 this.updateMarkdown();
26628 this.markdownEl.removeClass('d-none');
26629 this.editing = false;
26632 updateMarkdown : function()
26634 if (this.getValue() == '') {
26635 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26639 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26642 resizeTextArea: function () {
26645 Roo.log([sh, this.getValue().split("\n").length * 30]);
26646 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26648 setValue : function(val)
26650 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26651 if (!this.editing) {
26652 this.updateMarkdown();
26658 if (!this.editing) {
26659 this.toggleTextEdit();
26667 * @class Roo.bootstrap.Table.AbstractSelectionModel
26668 * @extends Roo.util.Observable
26669 * Abstract base class for grid SelectionModels. It provides the interface that should be
26670 * implemented by descendant classes. This class should not be directly instantiated.
26673 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26674 this.locked = false;
26675 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26679 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26680 /** @ignore Called by the grid automatically. Do not call directly. */
26681 init : function(grid){
26687 * Locks the selections.
26690 this.locked = true;
26694 * Unlocks the selections.
26696 unlock : function(){
26697 this.locked = false;
26701 * Returns true if the selections are locked.
26702 * @return {Boolean}
26704 isLocked : function(){
26705 return this.locked;
26709 initEvents : function ()
26715 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26716 * @class Roo.bootstrap.Table.RowSelectionModel
26717 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26718 * It supports multiple selections and keyboard selection/navigation.
26720 * @param {Object} config
26723 Roo.bootstrap.Table.RowSelectionModel = function(config){
26724 Roo.apply(this, config);
26725 this.selections = new Roo.util.MixedCollection(false, function(o){
26730 this.lastActive = false;
26734 * @event selectionchange
26735 * Fires when the selection changes
26736 * @param {SelectionModel} this
26738 "selectionchange" : true,
26740 * @event afterselectionchange
26741 * Fires after the selection changes (eg. by key press or clicking)
26742 * @param {SelectionModel} this
26744 "afterselectionchange" : true,
26746 * @event beforerowselect
26747 * Fires when a row is selected being selected, return false to cancel.
26748 * @param {SelectionModel} this
26749 * @param {Number} rowIndex The selected index
26750 * @param {Boolean} keepExisting False if other selections will be cleared
26752 "beforerowselect" : true,
26755 * Fires when a row is selected.
26756 * @param {SelectionModel} this
26757 * @param {Number} rowIndex The selected index
26758 * @param {Roo.data.Record} r The record
26760 "rowselect" : true,
26762 * @event rowdeselect
26763 * Fires when a row is deselected.
26764 * @param {SelectionModel} this
26765 * @param {Number} rowIndex The selected index
26767 "rowdeselect" : true
26769 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26770 this.locked = false;
26773 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26775 * @cfg {Boolean} singleSelect
26776 * True to allow selection of only one row at a time (defaults to false)
26778 singleSelect : false,
26781 initEvents : function()
26784 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26785 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26786 //}else{ // allow click to work like normal
26787 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26789 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26790 this.grid.on("rowclick", this.handleMouseDown, this);
26792 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26793 "up" : function(e){
26795 this.selectPrevious(e.shiftKey);
26796 }else if(this.last !== false && this.lastActive !== false){
26797 var last = this.last;
26798 this.selectRange(this.last, this.lastActive-1);
26799 this.grid.getView().focusRow(this.lastActive);
26800 if(last !== false){
26804 this.selectFirstRow();
26806 this.fireEvent("afterselectionchange", this);
26808 "down" : function(e){
26810 this.selectNext(e.shiftKey);
26811 }else if(this.last !== false && this.lastActive !== false){
26812 var last = this.last;
26813 this.selectRange(this.last, this.lastActive+1);
26814 this.grid.getView().focusRow(this.lastActive);
26815 if(last !== false){
26819 this.selectFirstRow();
26821 this.fireEvent("afterselectionchange", this);
26825 this.grid.store.on('load', function(){
26826 this.selections.clear();
26829 var view = this.grid.view;
26830 view.on("refresh", this.onRefresh, this);
26831 view.on("rowupdated", this.onRowUpdated, this);
26832 view.on("rowremoved", this.onRemove, this);
26837 onRefresh : function()
26839 var ds = this.grid.store, i, v = this.grid.view;
26840 var s = this.selections;
26841 s.each(function(r){
26842 if((i = ds.indexOfId(r.id)) != -1){
26851 onRemove : function(v, index, r){
26852 this.selections.remove(r);
26856 onRowUpdated : function(v, index, r){
26857 if(this.isSelected(r)){
26858 v.onRowSelect(index);
26864 * @param {Array} records The records to select
26865 * @param {Boolean} keepExisting (optional) True to keep existing selections
26867 selectRecords : function(records, keepExisting)
26870 this.clearSelections();
26872 var ds = this.grid.store;
26873 for(var i = 0, len = records.length; i < len; i++){
26874 this.selectRow(ds.indexOf(records[i]), true);
26879 * Gets the number of selected rows.
26882 getCount : function(){
26883 return this.selections.length;
26887 * Selects the first row in the grid.
26889 selectFirstRow : function(){
26894 * Select the last row.
26895 * @param {Boolean} keepExisting (optional) True to keep existing selections
26897 selectLastRow : function(keepExisting){
26898 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26899 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26903 * Selects the row immediately following the last selected row.
26904 * @param {Boolean} keepExisting (optional) True to keep existing selections
26906 selectNext : function(keepExisting)
26908 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26909 this.selectRow(this.last+1, keepExisting);
26910 this.grid.getView().focusRow(this.last);
26915 * Selects the row that precedes the last selected row.
26916 * @param {Boolean} keepExisting (optional) True to keep existing selections
26918 selectPrevious : function(keepExisting){
26920 this.selectRow(this.last-1, keepExisting);
26921 this.grid.getView().focusRow(this.last);
26926 * Returns the selected records
26927 * @return {Array} Array of selected records
26929 getSelections : function(){
26930 return [].concat(this.selections.items);
26934 * Returns the first selected record.
26937 getSelected : function(){
26938 return this.selections.itemAt(0);
26943 * Clears all selections.
26945 clearSelections : function(fast)
26951 var ds = this.grid.store;
26952 var s = this.selections;
26953 s.each(function(r){
26954 this.deselectRow(ds.indexOfId(r.id));
26958 this.selections.clear();
26965 * Selects all rows.
26967 selectAll : function(){
26971 this.selections.clear();
26972 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26973 this.selectRow(i, true);
26978 * Returns True if there is a selection.
26979 * @return {Boolean}
26981 hasSelection : function(){
26982 return this.selections.length > 0;
26986 * Returns True if the specified row is selected.
26987 * @param {Number/Record} record The record or index of the record to check
26988 * @return {Boolean}
26990 isSelected : function(index){
26991 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26992 return (r && this.selections.key(r.id) ? true : false);
26996 * Returns True if the specified record id is selected.
26997 * @param {String} id The id of record to check
26998 * @return {Boolean}
27000 isIdSelected : function(id){
27001 return (this.selections.key(id) ? true : false);
27006 handleMouseDBClick : function(e, t){
27010 handleMouseDown : function(e, t)
27012 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27013 if(this.isLocked() || rowIndex < 0 ){
27016 if(e.shiftKey && this.last !== false){
27017 var last = this.last;
27018 this.selectRange(last, rowIndex, e.ctrlKey);
27019 this.last = last; // reset the last
27023 var isSelected = this.isSelected(rowIndex);
27024 //Roo.log("select row:" + rowIndex);
27026 this.deselectRow(rowIndex);
27028 this.selectRow(rowIndex, true);
27032 if(e.button !== 0 && isSelected){
27033 alert('rowIndex 2: ' + rowIndex);
27034 view.focusRow(rowIndex);
27035 }else if(e.ctrlKey && isSelected){
27036 this.deselectRow(rowIndex);
27037 }else if(!isSelected){
27038 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27039 view.focusRow(rowIndex);
27043 this.fireEvent("afterselectionchange", this);
27046 handleDragableRowClick : function(grid, rowIndex, e)
27048 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27049 this.selectRow(rowIndex, false);
27050 grid.view.focusRow(rowIndex);
27051 this.fireEvent("afterselectionchange", this);
27056 * Selects multiple rows.
27057 * @param {Array} rows Array of the indexes of the row to select
27058 * @param {Boolean} keepExisting (optional) True to keep existing selections
27060 selectRows : function(rows, keepExisting){
27062 this.clearSelections();
27064 for(var i = 0, len = rows.length; i < len; i++){
27065 this.selectRow(rows[i], true);
27070 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27071 * @param {Number} startRow The index of the first row in the range
27072 * @param {Number} endRow The index of the last row in the range
27073 * @param {Boolean} keepExisting (optional) True to retain existing selections
27075 selectRange : function(startRow, endRow, keepExisting){
27080 this.clearSelections();
27082 if(startRow <= endRow){
27083 for(var i = startRow; i <= endRow; i++){
27084 this.selectRow(i, true);
27087 for(var i = startRow; i >= endRow; i--){
27088 this.selectRow(i, true);
27094 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27095 * @param {Number} startRow The index of the first row in the range
27096 * @param {Number} endRow The index of the last row in the range
27098 deselectRange : function(startRow, endRow, preventViewNotify){
27102 for(var i = startRow; i <= endRow; i++){
27103 this.deselectRow(i, preventViewNotify);
27109 * @param {Number} row The index of the row to select
27110 * @param {Boolean} keepExisting (optional) True to keep existing selections
27112 selectRow : function(index, keepExisting, preventViewNotify)
27114 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27117 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27118 if(!keepExisting || this.singleSelect){
27119 this.clearSelections();
27122 var r = this.grid.store.getAt(index);
27123 //console.log('selectRow - record id :' + r.id);
27125 this.selections.add(r);
27126 this.last = this.lastActive = index;
27127 if(!preventViewNotify){
27128 var proxy = new Roo.Element(
27129 this.grid.getRowDom(index)
27131 proxy.addClass('bg-info info');
27133 this.fireEvent("rowselect", this, index, r);
27134 this.fireEvent("selectionchange", this);
27140 * @param {Number} row The index of the row to deselect
27142 deselectRow : function(index, preventViewNotify)
27147 if(this.last == index){
27150 if(this.lastActive == index){
27151 this.lastActive = false;
27154 var r = this.grid.store.getAt(index);
27159 this.selections.remove(r);
27160 //.console.log('deselectRow - record id :' + r.id);
27161 if(!preventViewNotify){
27163 var proxy = new Roo.Element(
27164 this.grid.getRowDom(index)
27166 proxy.removeClass('bg-info info');
27168 this.fireEvent("rowdeselect", this, index);
27169 this.fireEvent("selectionchange", this);
27173 restoreLast : function(){
27175 this.last = this._last;
27180 acceptsNav : function(row, col, cm){
27181 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27185 onEditorKey : function(field, e){
27186 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27191 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27193 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27195 }else if(k == e.ENTER && !e.ctrlKey){
27199 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27201 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27203 }else if(k == e.ESC){
27207 g.startEditing(newCell[0], newCell[1]);
27213 * Ext JS Library 1.1.1
27214 * Copyright(c) 2006-2007, Ext JS, LLC.
27216 * Originally Released Under LGPL - original licence link has changed is not relivant.
27219 * <script type="text/javascript">
27223 * @class Roo.bootstrap.PagingToolbar
27224 * @extends Roo.bootstrap.NavSimplebar
27225 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27227 * Create a new PagingToolbar
27228 * @param {Object} config The config object
27229 * @param {Roo.data.Store} store
27231 Roo.bootstrap.PagingToolbar = function(config)
27233 // old args format still supported... - xtype is prefered..
27234 // created from xtype...
27236 this.ds = config.dataSource;
27238 if (config.store && !this.ds) {
27239 this.store= Roo.factory(config.store, Roo.data);
27240 this.ds = this.store;
27241 this.ds.xmodule = this.xmodule || false;
27244 this.toolbarItems = [];
27245 if (config.items) {
27246 this.toolbarItems = config.items;
27249 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27254 this.bind(this.ds);
27257 if (Roo.bootstrap.version == 4) {
27258 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27260 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27265 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27267 * @cfg {Roo.data.Store} dataSource
27268 * The underlying data store providing the paged data
27271 * @cfg {String/HTMLElement/Element} container
27272 * container The id or element that will contain the toolbar
27275 * @cfg {Boolean} displayInfo
27276 * True to display the displayMsg (defaults to false)
27279 * @cfg {Number} pageSize
27280 * The number of records to display per page (defaults to 20)
27284 * @cfg {String} displayMsg
27285 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27287 displayMsg : 'Displaying {0} - {1} of {2}',
27289 * @cfg {String} emptyMsg
27290 * The message to display when no records are found (defaults to "No data to display")
27292 emptyMsg : 'No data to display',
27294 * Customizable piece of the default paging text (defaults to "Page")
27297 beforePageText : "Page",
27299 * Customizable piece of the default paging text (defaults to "of %0")
27302 afterPageText : "of {0}",
27304 * Customizable piece of the default paging text (defaults to "First Page")
27307 firstText : "First Page",
27309 * Customizable piece of the default paging text (defaults to "Previous Page")
27312 prevText : "Previous Page",
27314 * Customizable piece of the default paging text (defaults to "Next Page")
27317 nextText : "Next Page",
27319 * Customizable piece of the default paging text (defaults to "Last Page")
27322 lastText : "Last Page",
27324 * Customizable piece of the default paging text (defaults to "Refresh")
27327 refreshText : "Refresh",
27331 onRender : function(ct, position)
27333 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27334 this.navgroup.parentId = this.id;
27335 this.navgroup.onRender(this.el, null);
27336 // add the buttons to the navgroup
27338 if(this.displayInfo){
27339 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27340 this.displayEl = this.el.select('.x-paging-info', true).first();
27341 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27342 // this.displayEl = navel.el.select('span',true).first();
27348 Roo.each(_this.buttons, function(e){ // this might need to use render????
27349 Roo.factory(e).render(_this.el);
27353 Roo.each(_this.toolbarItems, function(e) {
27354 _this.navgroup.addItem(e);
27358 this.first = this.navgroup.addItem({
27359 tooltip: this.firstText,
27360 cls: "prev btn-outline-secondary",
27361 html : ' <i class="fa fa-step-backward"></i>',
27363 preventDefault: true,
27364 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27367 this.prev = this.navgroup.addItem({
27368 tooltip: this.prevText,
27369 cls: "prev btn-outline-secondary",
27370 html : ' <i class="fa fa-backward"></i>',
27372 preventDefault: true,
27373 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27375 //this.addSeparator();
27378 var field = this.navgroup.addItem( {
27380 cls : 'x-paging-position btn-outline-secondary',
27382 html : this.beforePageText +
27383 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27384 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27387 this.field = field.el.select('input', true).first();
27388 this.field.on("keydown", this.onPagingKeydown, this);
27389 this.field.on("focus", function(){this.dom.select();});
27392 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27393 //this.field.setHeight(18);
27394 //this.addSeparator();
27395 this.next = this.navgroup.addItem({
27396 tooltip: this.nextText,
27397 cls: "next btn-outline-secondary",
27398 html : ' <i class="fa fa-forward"></i>',
27400 preventDefault: true,
27401 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27403 this.last = this.navgroup.addItem({
27404 tooltip: this.lastText,
27405 html : ' <i class="fa fa-step-forward"></i>',
27406 cls: "next btn-outline-secondary",
27408 preventDefault: true,
27409 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27411 //this.addSeparator();
27412 this.loading = this.navgroup.addItem({
27413 tooltip: this.refreshText,
27414 cls: "btn-outline-secondary",
27415 html : ' <i class="fa fa-refresh"></i>',
27416 preventDefault: true,
27417 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27423 updateInfo : function(){
27424 if(this.displayEl){
27425 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27426 var msg = count == 0 ?
27430 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27432 this.displayEl.update(msg);
27437 onLoad : function(ds, r, o)
27439 this.cursor = o.params && o.params.start ? o.params.start : 0;
27441 var d = this.getPageData(),
27446 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27447 this.field.dom.value = ap;
27448 this.first.setDisabled(ap == 1);
27449 this.prev.setDisabled(ap == 1);
27450 this.next.setDisabled(ap == ps);
27451 this.last.setDisabled(ap == ps);
27452 this.loading.enable();
27457 getPageData : function(){
27458 var total = this.ds.getTotalCount();
27461 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27462 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27467 onLoadError : function(){
27468 this.loading.enable();
27472 onPagingKeydown : function(e){
27473 var k = e.getKey();
27474 var d = this.getPageData();
27476 var v = this.field.dom.value, pageNum;
27477 if(!v || isNaN(pageNum = parseInt(v, 10))){
27478 this.field.dom.value = d.activePage;
27481 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27482 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27485 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))
27487 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27488 this.field.dom.value = pageNum;
27489 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27492 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27494 var v = this.field.dom.value, pageNum;
27495 var increment = (e.shiftKey) ? 10 : 1;
27496 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27499 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27500 this.field.dom.value = d.activePage;
27503 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27505 this.field.dom.value = parseInt(v, 10) + increment;
27506 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27507 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27514 beforeLoad : function(){
27516 this.loading.disable();
27521 onClick : function(which){
27530 ds.load({params:{start: 0, limit: this.pageSize}});
27533 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27536 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27539 var total = ds.getTotalCount();
27540 var extra = total % this.pageSize;
27541 var lastStart = extra ? (total - extra) : total-this.pageSize;
27542 ds.load({params:{start: lastStart, limit: this.pageSize}});
27545 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27551 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27552 * @param {Roo.data.Store} store The data store to unbind
27554 unbind : function(ds){
27555 ds.un("beforeload", this.beforeLoad, this);
27556 ds.un("load", this.onLoad, this);
27557 ds.un("loadexception", this.onLoadError, this);
27558 ds.un("remove", this.updateInfo, this);
27559 ds.un("add", this.updateInfo, this);
27560 this.ds = undefined;
27564 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27565 * @param {Roo.data.Store} store The data store to bind
27567 bind : function(ds){
27568 ds.on("beforeload", this.beforeLoad, this);
27569 ds.on("load", this.onLoad, this);
27570 ds.on("loadexception", this.onLoadError, this);
27571 ds.on("remove", this.updateInfo, this);
27572 ds.on("add", this.updateInfo, this);
27583 * @class Roo.bootstrap.MessageBar
27584 * @extends Roo.bootstrap.Component
27585 * Bootstrap MessageBar class
27586 * @cfg {String} html contents of the MessageBar
27587 * @cfg {String} weight (info | success | warning | danger) default info
27588 * @cfg {String} beforeClass insert the bar before the given class
27589 * @cfg {Boolean} closable (true | false) default false
27590 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27593 * Create a new Element
27594 * @param {Object} config The config object
27597 Roo.bootstrap.MessageBar = function(config){
27598 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27601 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27607 beforeClass: 'bootstrap-sticky-wrap',
27609 getAutoCreate : function(){
27613 cls: 'alert alert-dismissable alert-' + this.weight,
27618 html: this.html || ''
27624 cfg.cls += ' alert-messages-fixed';
27638 onRender : function(ct, position)
27640 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27643 var cfg = Roo.apply({}, this.getAutoCreate());
27647 cfg.cls += ' ' + this.cls;
27650 cfg.style = this.style;
27652 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27654 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27657 this.el.select('>button.close').on('click', this.hide, this);
27663 if (!this.rendered) {
27669 this.fireEvent('show', this);
27675 if (!this.rendered) {
27681 this.fireEvent('hide', this);
27684 update : function()
27686 // var e = this.el.dom.firstChild;
27688 // if(this.closable){
27689 // e = e.nextSibling;
27692 // e.data = this.html || '';
27694 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27710 * @class Roo.bootstrap.Graph
27711 * @extends Roo.bootstrap.Component
27712 * Bootstrap Graph class
27716 @cfg {String} graphtype bar | vbar | pie
27717 @cfg {number} g_x coodinator | centre x (pie)
27718 @cfg {number} g_y coodinator | centre y (pie)
27719 @cfg {number} g_r radius (pie)
27720 @cfg {number} g_height height of the chart (respected by all elements in the set)
27721 @cfg {number} g_width width of the chart (respected by all elements in the set)
27722 @cfg {Object} title The title of the chart
27725 -opts (object) options for the chart
27727 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27728 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27730 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.
27731 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27733 o stretch (boolean)
27735 -opts (object) options for the pie
27738 o startAngle (number)
27739 o endAngle (number)
27743 * Create a new Input
27744 * @param {Object} config The config object
27747 Roo.bootstrap.Graph = function(config){
27748 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27754 * The img click event for the img.
27755 * @param {Roo.EventObject} e
27761 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27772 //g_colors: this.colors,
27779 getAutoCreate : function(){
27790 onRender : function(ct,position){
27793 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27795 if (typeof(Raphael) == 'undefined') {
27796 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27800 this.raphael = Raphael(this.el.dom);
27802 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27803 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27804 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27805 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27807 r.text(160, 10, "Single Series Chart").attr(txtattr);
27808 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27809 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27810 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27812 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27813 r.barchart(330, 10, 300, 220, data1);
27814 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27815 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27818 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27819 // r.barchart(30, 30, 560, 250, xdata, {
27820 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27821 // axis : "0 0 1 1",
27822 // axisxlabels : xdata
27823 // //yvalues : cols,
27826 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27828 // this.load(null,xdata,{
27829 // axis : "0 0 1 1",
27830 // axisxlabels : xdata
27835 load : function(graphtype,xdata,opts)
27837 this.raphael.clear();
27839 graphtype = this.graphtype;
27844 var r = this.raphael,
27845 fin = function () {
27846 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27848 fout = function () {
27849 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27851 pfin = function() {
27852 this.sector.stop();
27853 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27856 this.label[0].stop();
27857 this.label[0].attr({ r: 7.5 });
27858 this.label[1].attr({ "font-weight": 800 });
27861 pfout = function() {
27862 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27865 this.label[0].animate({ r: 5 }, 500, "bounce");
27866 this.label[1].attr({ "font-weight": 400 });
27872 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27875 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27878 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27879 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27881 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27888 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27893 setTitle: function(o)
27898 initEvents: function() {
27901 this.el.on('click', this.onClick, this);
27905 onClick : function(e)
27907 Roo.log('img onclick');
27908 this.fireEvent('click', this, e);
27920 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27923 * @class Roo.bootstrap.dash.NumberBox
27924 * @extends Roo.bootstrap.Component
27925 * Bootstrap NumberBox class
27926 * @cfg {String} headline Box headline
27927 * @cfg {String} content Box content
27928 * @cfg {String} icon Box icon
27929 * @cfg {String} footer Footer text
27930 * @cfg {String} fhref Footer href
27933 * Create a new NumberBox
27934 * @param {Object} config The config object
27938 Roo.bootstrap.dash.NumberBox = function(config){
27939 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27943 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27952 getAutoCreate : function(){
27956 cls : 'small-box ',
27964 cls : 'roo-headline',
27965 html : this.headline
27969 cls : 'roo-content',
27970 html : this.content
27984 cls : 'ion ' + this.icon
27993 cls : 'small-box-footer',
27994 href : this.fhref || '#',
27998 cfg.cn.push(footer);
28005 onRender : function(ct,position){
28006 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28013 setHeadline: function (value)
28015 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28018 setFooter: function (value, href)
28020 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28023 this.el.select('a.small-box-footer',true).first().attr('href', href);
28028 setContent: function (value)
28030 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28033 initEvents: function()
28047 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28050 * @class Roo.bootstrap.dash.TabBox
28051 * @extends Roo.bootstrap.Component
28052 * Bootstrap TabBox class
28053 * @cfg {String} title Title of the TabBox
28054 * @cfg {String} icon Icon of the TabBox
28055 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28056 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28059 * Create a new TabBox
28060 * @param {Object} config The config object
28064 Roo.bootstrap.dash.TabBox = function(config){
28065 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28070 * When a pane is added
28071 * @param {Roo.bootstrap.dash.TabPane} pane
28075 * @event activatepane
28076 * When a pane is activated
28077 * @param {Roo.bootstrap.dash.TabPane} pane
28079 "activatepane" : true
28087 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28092 tabScrollable : false,
28094 getChildContainer : function()
28096 return this.el.select('.tab-content', true).first();
28099 getAutoCreate : function(){
28103 cls: 'pull-left header',
28111 cls: 'fa ' + this.icon
28117 cls: 'nav nav-tabs pull-right',
28123 if(this.tabScrollable){
28130 cls: 'nav nav-tabs pull-right',
28141 cls: 'nav-tabs-custom',
28146 cls: 'tab-content no-padding',
28154 initEvents : function()
28156 //Roo.log('add add pane handler');
28157 this.on('addpane', this.onAddPane, this);
28160 * Updates the box title
28161 * @param {String} html to set the title to.
28163 setTitle : function(value)
28165 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28167 onAddPane : function(pane)
28169 this.panes.push(pane);
28170 //Roo.log('addpane');
28172 // tabs are rendere left to right..
28173 if(!this.showtabs){
28177 var ctr = this.el.select('.nav-tabs', true).first();
28180 var existing = ctr.select('.nav-tab',true);
28181 var qty = existing.getCount();;
28184 var tab = ctr.createChild({
28186 cls : 'nav-tab' + (qty ? '' : ' active'),
28194 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28197 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28199 pane.el.addClass('active');
28204 onTabClick : function(ev,un,ob,pane)
28206 //Roo.log('tab - prev default');
28207 ev.preventDefault();
28210 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28211 pane.tab.addClass('active');
28212 //Roo.log(pane.title);
28213 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28214 // technically we should have a deactivate event.. but maybe add later.
28215 // and it should not de-activate the selected tab...
28216 this.fireEvent('activatepane', pane);
28217 pane.el.addClass('active');
28218 pane.fireEvent('activate');
28223 getActivePane : function()
28226 Roo.each(this.panes, function(p) {
28227 if(p.el.hasClass('active')){
28248 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28250 * @class Roo.bootstrap.TabPane
28251 * @extends Roo.bootstrap.Component
28252 * Bootstrap TabPane class
28253 * @cfg {Boolean} active (false | true) Default false
28254 * @cfg {String} title title of panel
28258 * Create a new TabPane
28259 * @param {Object} config The config object
28262 Roo.bootstrap.dash.TabPane = function(config){
28263 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28269 * When a pane is activated
28270 * @param {Roo.bootstrap.dash.TabPane} pane
28277 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28282 // the tabBox that this is attached to.
28285 getAutoCreate : function()
28293 cfg.cls += ' active';
28298 initEvents : function()
28300 //Roo.log('trigger add pane handler');
28301 this.parent().fireEvent('addpane', this)
28305 * Updates the tab title
28306 * @param {String} html to set the title to.
28308 setTitle: function(str)
28314 this.tab.select('a', true).first().dom.innerHTML = str;
28331 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28334 * @class Roo.bootstrap.menu.Menu
28335 * @extends Roo.bootstrap.Component
28336 * Bootstrap Menu class - container for Menu
28337 * @cfg {String} html Text of the menu
28338 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28339 * @cfg {String} icon Font awesome icon
28340 * @cfg {String} pos Menu align to (top | bottom) default bottom
28344 * Create a new Menu
28345 * @param {Object} config The config object
28349 Roo.bootstrap.menu.Menu = function(config){
28350 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28354 * @event beforeshow
28355 * Fires before this menu is displayed
28356 * @param {Roo.bootstrap.menu.Menu} this
28360 * @event beforehide
28361 * Fires before this menu is hidden
28362 * @param {Roo.bootstrap.menu.Menu} this
28367 * Fires after this menu is displayed
28368 * @param {Roo.bootstrap.menu.Menu} this
28373 * Fires after this menu is hidden
28374 * @param {Roo.bootstrap.menu.Menu} this
28379 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28380 * @param {Roo.bootstrap.menu.Menu} this
28381 * @param {Roo.EventObject} e
28388 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28392 weight : 'default',
28397 getChildContainer : function() {
28398 if(this.isSubMenu){
28402 return this.el.select('ul.dropdown-menu', true).first();
28405 getAutoCreate : function()
28410 cls : 'roo-menu-text',
28418 cls : 'fa ' + this.icon
28429 cls : 'dropdown-button btn btn-' + this.weight,
28434 cls : 'dropdown-toggle btn btn-' + this.weight,
28444 cls : 'dropdown-menu'
28450 if(this.pos == 'top'){
28451 cfg.cls += ' dropup';
28454 if(this.isSubMenu){
28457 cls : 'dropdown-menu'
28464 onRender : function(ct, position)
28466 this.isSubMenu = ct.hasClass('dropdown-submenu');
28468 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28471 initEvents : function()
28473 if(this.isSubMenu){
28477 this.hidden = true;
28479 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28480 this.triggerEl.on('click', this.onTriggerPress, this);
28482 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28483 this.buttonEl.on('click', this.onClick, this);
28489 if(this.isSubMenu){
28493 return this.el.select('ul.dropdown-menu', true).first();
28496 onClick : function(e)
28498 this.fireEvent("click", this, e);
28501 onTriggerPress : function(e)
28503 if (this.isVisible()) {
28510 isVisible : function(){
28511 return !this.hidden;
28516 this.fireEvent("beforeshow", this);
28518 this.hidden = false;
28519 this.el.addClass('open');
28521 Roo.get(document).on("mouseup", this.onMouseUp, this);
28523 this.fireEvent("show", this);
28530 this.fireEvent("beforehide", this);
28532 this.hidden = true;
28533 this.el.removeClass('open');
28535 Roo.get(document).un("mouseup", this.onMouseUp);
28537 this.fireEvent("hide", this);
28540 onMouseUp : function()
28554 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28557 * @class Roo.bootstrap.menu.Item
28558 * @extends Roo.bootstrap.Component
28559 * Bootstrap MenuItem class
28560 * @cfg {Boolean} submenu (true | false) default false
28561 * @cfg {String} html text of the item
28562 * @cfg {String} href the link
28563 * @cfg {Boolean} disable (true | false) default false
28564 * @cfg {Boolean} preventDefault (true | false) default true
28565 * @cfg {String} icon Font awesome icon
28566 * @cfg {String} pos Submenu align to (left | right) default right
28570 * Create a new Item
28571 * @param {Object} config The config object
28575 Roo.bootstrap.menu.Item = function(config){
28576 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28580 * Fires when the mouse is hovering over this menu
28581 * @param {Roo.bootstrap.menu.Item} this
28582 * @param {Roo.EventObject} e
28587 * Fires when the mouse exits this menu
28588 * @param {Roo.bootstrap.menu.Item} this
28589 * @param {Roo.EventObject} e
28595 * The raw click event for the entire grid.
28596 * @param {Roo.EventObject} e
28602 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28607 preventDefault: true,
28612 getAutoCreate : function()
28617 cls : 'roo-menu-item-text',
28625 cls : 'fa ' + this.icon
28634 href : this.href || '#',
28641 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28645 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28647 if(this.pos == 'left'){
28648 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28655 initEvents : function()
28657 this.el.on('mouseover', this.onMouseOver, this);
28658 this.el.on('mouseout', this.onMouseOut, this);
28660 this.el.select('a', true).first().on('click', this.onClick, this);
28664 onClick : function(e)
28666 if(this.preventDefault){
28667 e.preventDefault();
28670 this.fireEvent("click", this, e);
28673 onMouseOver : function(e)
28675 if(this.submenu && this.pos == 'left'){
28676 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28679 this.fireEvent("mouseover", this, e);
28682 onMouseOut : function(e)
28684 this.fireEvent("mouseout", this, e);
28696 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28699 * @class Roo.bootstrap.menu.Separator
28700 * @extends Roo.bootstrap.Component
28701 * Bootstrap Separator class
28704 * Create a new Separator
28705 * @param {Object} config The config object
28709 Roo.bootstrap.menu.Separator = function(config){
28710 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28713 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28715 getAutoCreate : function(){
28736 * @class Roo.bootstrap.Tooltip
28737 * Bootstrap Tooltip class
28738 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28739 * to determine which dom element triggers the tooltip.
28741 * It needs to add support for additional attributes like tooltip-position
28744 * Create a new Toolti
28745 * @param {Object} config The config object
28748 Roo.bootstrap.Tooltip = function(config){
28749 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28751 this.alignment = Roo.bootstrap.Tooltip.alignment;
28753 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28754 this.alignment = config.alignment;
28759 Roo.apply(Roo.bootstrap.Tooltip, {
28761 * @function init initialize tooltip monitoring.
28765 currentTip : false,
28766 currentRegion : false,
28772 Roo.get(document).on('mouseover', this.enter ,this);
28773 Roo.get(document).on('mouseout', this.leave, this);
28776 this.currentTip = new Roo.bootstrap.Tooltip();
28779 enter : function(ev)
28781 var dom = ev.getTarget();
28783 //Roo.log(['enter',dom]);
28784 var el = Roo.fly(dom);
28785 if (this.currentEl) {
28787 //Roo.log(this.currentEl);
28788 //Roo.log(this.currentEl.contains(dom));
28789 if (this.currentEl == el) {
28792 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28798 if (this.currentTip.el) {
28799 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28803 if(!el || el.dom == document){
28809 // you can not look for children, as if el is the body.. then everythign is the child..
28810 if (!el.attr('tooltip')) { //
28811 if (!el.select("[tooltip]").elements.length) {
28814 // is the mouse over this child...?
28815 bindEl = el.select("[tooltip]").first();
28816 var xy = ev.getXY();
28817 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28818 //Roo.log("not in region.");
28821 //Roo.log("child element over..");
28824 this.currentEl = bindEl;
28825 this.currentTip.bind(bindEl);
28826 this.currentRegion = Roo.lib.Region.getRegion(dom);
28827 this.currentTip.enter();
28830 leave : function(ev)
28832 var dom = ev.getTarget();
28833 //Roo.log(['leave',dom]);
28834 if (!this.currentEl) {
28839 if (dom != this.currentEl.dom) {
28842 var xy = ev.getXY();
28843 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28846 // only activate leave if mouse cursor is outside... bounding box..
28851 if (this.currentTip) {
28852 this.currentTip.leave();
28854 //Roo.log('clear currentEl');
28855 this.currentEl = false;
28860 'left' : ['r-l', [-2,0], 'right'],
28861 'right' : ['l-r', [2,0], 'left'],
28862 'bottom' : ['t-b', [0,2], 'top'],
28863 'top' : [ 'b-t', [0,-2], 'bottom']
28869 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28874 delay : null, // can be { show : 300 , hide: 500}
28878 hoverState : null, //???
28880 placement : 'bottom',
28884 getAutoCreate : function(){
28891 cls : 'tooltip-arrow arrow'
28894 cls : 'tooltip-inner'
28901 bind : function(el)
28906 initEvents : function()
28908 this.arrowEl = this.el.select('.arrow', true).first();
28909 this.innerEl = this.el.select('.tooltip-inner', true).first();
28912 enter : function () {
28914 if (this.timeout != null) {
28915 clearTimeout(this.timeout);
28918 this.hoverState = 'in';
28919 //Roo.log("enter - show");
28920 if (!this.delay || !this.delay.show) {
28925 this.timeout = setTimeout(function () {
28926 if (_t.hoverState == 'in') {
28929 }, this.delay.show);
28933 clearTimeout(this.timeout);
28935 this.hoverState = 'out';
28936 if (!this.delay || !this.delay.hide) {
28942 this.timeout = setTimeout(function () {
28943 //Roo.log("leave - timeout");
28945 if (_t.hoverState == 'out') {
28947 Roo.bootstrap.Tooltip.currentEl = false;
28952 show : function (msg)
28955 this.render(document.body);
28958 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28960 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28962 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28964 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28965 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28967 var placement = typeof this.placement == 'function' ?
28968 this.placement.call(this, this.el, on_el) :
28971 var autoToken = /\s?auto?\s?/i;
28972 var autoPlace = autoToken.test(placement);
28974 placement = placement.replace(autoToken, '') || 'top';
28978 //this.el.setXY([0,0]);
28980 //this.el.dom.style.display='block';
28982 //this.el.appendTo(on_el);
28984 var p = this.getPosition();
28985 var box = this.el.getBox();
28991 var align = this.alignment[placement];
28993 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28995 if(placement == 'top' || placement == 'bottom'){
28997 placement = 'right';
29000 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29001 placement = 'left';
29004 var scroll = Roo.select('body', true).first().getScroll();
29006 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29010 align = this.alignment[placement];
29012 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29016 this.el.alignTo(this.bindEl, align[0],align[1]);
29017 //var arrow = this.el.select('.arrow',true).first();
29018 //arrow.set(align[2],
29020 this.el.addClass(placement);
29021 this.el.addClass("bs-tooltip-"+ placement);
29023 this.el.addClass('in fade show');
29025 this.hoverState = null;
29027 if (this.el.hasClass('fade')) {
29042 //this.el.setXY([0,0]);
29043 this.el.removeClass(['show', 'in']);
29059 * @class Roo.bootstrap.LocationPicker
29060 * @extends Roo.bootstrap.Component
29061 * Bootstrap LocationPicker class
29062 * @cfg {Number} latitude Position when init default 0
29063 * @cfg {Number} longitude Position when init default 0
29064 * @cfg {Number} zoom default 15
29065 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29066 * @cfg {Boolean} mapTypeControl default false
29067 * @cfg {Boolean} disableDoubleClickZoom default false
29068 * @cfg {Boolean} scrollwheel default true
29069 * @cfg {Boolean} streetViewControl default false
29070 * @cfg {Number} radius default 0
29071 * @cfg {String} locationName
29072 * @cfg {Boolean} draggable default true
29073 * @cfg {Boolean} enableAutocomplete default false
29074 * @cfg {Boolean} enableReverseGeocode default true
29075 * @cfg {String} markerTitle
29078 * Create a new LocationPicker
29079 * @param {Object} config The config object
29083 Roo.bootstrap.LocationPicker = function(config){
29085 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29090 * Fires when the picker initialized.
29091 * @param {Roo.bootstrap.LocationPicker} this
29092 * @param {Google Location} location
29096 * @event positionchanged
29097 * Fires when the picker position changed.
29098 * @param {Roo.bootstrap.LocationPicker} this
29099 * @param {Google Location} location
29101 positionchanged : true,
29104 * Fires when the map resize.
29105 * @param {Roo.bootstrap.LocationPicker} this
29110 * Fires when the map show.
29111 * @param {Roo.bootstrap.LocationPicker} this
29116 * Fires when the map hide.
29117 * @param {Roo.bootstrap.LocationPicker} this
29122 * Fires when click the map.
29123 * @param {Roo.bootstrap.LocationPicker} this
29124 * @param {Map event} e
29128 * @event mapRightClick
29129 * Fires when right click the map.
29130 * @param {Roo.bootstrap.LocationPicker} this
29131 * @param {Map event} e
29133 mapRightClick : true,
29135 * @event markerClick
29136 * Fires when click the marker.
29137 * @param {Roo.bootstrap.LocationPicker} this
29138 * @param {Map event} e
29140 markerClick : true,
29142 * @event markerRightClick
29143 * Fires when right click the marker.
29144 * @param {Roo.bootstrap.LocationPicker} this
29145 * @param {Map event} e
29147 markerRightClick : true,
29149 * @event OverlayViewDraw
29150 * Fires when OverlayView Draw
29151 * @param {Roo.bootstrap.LocationPicker} this
29153 OverlayViewDraw : true,
29155 * @event OverlayViewOnAdd
29156 * Fires when OverlayView Draw
29157 * @param {Roo.bootstrap.LocationPicker} this
29159 OverlayViewOnAdd : true,
29161 * @event OverlayViewOnRemove
29162 * Fires when OverlayView Draw
29163 * @param {Roo.bootstrap.LocationPicker} this
29165 OverlayViewOnRemove : true,
29167 * @event OverlayViewShow
29168 * Fires when OverlayView Draw
29169 * @param {Roo.bootstrap.LocationPicker} this
29170 * @param {Pixel} cpx
29172 OverlayViewShow : true,
29174 * @event OverlayViewHide
29175 * Fires when OverlayView Draw
29176 * @param {Roo.bootstrap.LocationPicker} this
29178 OverlayViewHide : true,
29180 * @event loadexception
29181 * Fires when load google lib failed.
29182 * @param {Roo.bootstrap.LocationPicker} this
29184 loadexception : true
29189 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29191 gMapContext: false,
29197 mapTypeControl: false,
29198 disableDoubleClickZoom: false,
29200 streetViewControl: false,
29204 enableAutocomplete: false,
29205 enableReverseGeocode: true,
29208 getAutoCreate: function()
29213 cls: 'roo-location-picker'
29219 initEvents: function(ct, position)
29221 if(!this.el.getWidth() || this.isApplied()){
29225 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29230 initial: function()
29232 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29233 this.fireEvent('loadexception', this);
29237 if(!this.mapTypeId){
29238 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29241 this.gMapContext = this.GMapContext();
29243 this.initOverlayView();
29245 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29249 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29250 _this.setPosition(_this.gMapContext.marker.position);
29253 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29254 _this.fireEvent('mapClick', this, event);
29258 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29259 _this.fireEvent('mapRightClick', this, event);
29263 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29264 _this.fireEvent('markerClick', this, event);
29268 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29269 _this.fireEvent('markerRightClick', this, event);
29273 this.setPosition(this.gMapContext.location);
29275 this.fireEvent('initial', this, this.gMapContext.location);
29278 initOverlayView: function()
29282 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29286 _this.fireEvent('OverlayViewDraw', _this);
29291 _this.fireEvent('OverlayViewOnAdd', _this);
29294 onRemove: function()
29296 _this.fireEvent('OverlayViewOnRemove', _this);
29299 show: function(cpx)
29301 _this.fireEvent('OverlayViewShow', _this, cpx);
29306 _this.fireEvent('OverlayViewHide', _this);
29312 fromLatLngToContainerPixel: function(event)
29314 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29317 isApplied: function()
29319 return this.getGmapContext() == false ? false : true;
29322 getGmapContext: function()
29324 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29327 GMapContext: function()
29329 var position = new google.maps.LatLng(this.latitude, this.longitude);
29331 var _map = new google.maps.Map(this.el.dom, {
29334 mapTypeId: this.mapTypeId,
29335 mapTypeControl: this.mapTypeControl,
29336 disableDoubleClickZoom: this.disableDoubleClickZoom,
29337 scrollwheel: this.scrollwheel,
29338 streetViewControl: this.streetViewControl,
29339 locationName: this.locationName,
29340 draggable: this.draggable,
29341 enableAutocomplete: this.enableAutocomplete,
29342 enableReverseGeocode: this.enableReverseGeocode
29345 var _marker = new google.maps.Marker({
29346 position: position,
29348 title: this.markerTitle,
29349 draggable: this.draggable
29356 location: position,
29357 radius: this.radius,
29358 locationName: this.locationName,
29359 addressComponents: {
29360 formatted_address: null,
29361 addressLine1: null,
29362 addressLine2: null,
29364 streetNumber: null,
29368 stateOrProvince: null
29371 domContainer: this.el.dom,
29372 geodecoder: new google.maps.Geocoder()
29376 drawCircle: function(center, radius, options)
29378 if (this.gMapContext.circle != null) {
29379 this.gMapContext.circle.setMap(null);
29383 options = Roo.apply({}, options, {
29384 strokeColor: "#0000FF",
29385 strokeOpacity: .35,
29387 fillColor: "#0000FF",
29391 options.map = this.gMapContext.map;
29392 options.radius = radius;
29393 options.center = center;
29394 this.gMapContext.circle = new google.maps.Circle(options);
29395 return this.gMapContext.circle;
29401 setPosition: function(location)
29403 this.gMapContext.location = location;
29404 this.gMapContext.marker.setPosition(location);
29405 this.gMapContext.map.panTo(location);
29406 this.drawCircle(location, this.gMapContext.radius, {});
29410 if (this.gMapContext.settings.enableReverseGeocode) {
29411 this.gMapContext.geodecoder.geocode({
29412 latLng: this.gMapContext.location
29413 }, function(results, status) {
29415 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29416 _this.gMapContext.locationName = results[0].formatted_address;
29417 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29419 _this.fireEvent('positionchanged', this, location);
29426 this.fireEvent('positionchanged', this, location);
29431 google.maps.event.trigger(this.gMapContext.map, "resize");
29433 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29435 this.fireEvent('resize', this);
29438 setPositionByLatLng: function(latitude, longitude)
29440 this.setPosition(new google.maps.LatLng(latitude, longitude));
29443 getCurrentPosition: function()
29446 latitude: this.gMapContext.location.lat(),
29447 longitude: this.gMapContext.location.lng()
29451 getAddressName: function()
29453 return this.gMapContext.locationName;
29456 getAddressComponents: function()
29458 return this.gMapContext.addressComponents;
29461 address_component_from_google_geocode: function(address_components)
29465 for (var i = 0; i < address_components.length; i++) {
29466 var component = address_components[i];
29467 if (component.types.indexOf("postal_code") >= 0) {
29468 result.postalCode = component.short_name;
29469 } else if (component.types.indexOf("street_number") >= 0) {
29470 result.streetNumber = component.short_name;
29471 } else if (component.types.indexOf("route") >= 0) {
29472 result.streetName = component.short_name;
29473 } else if (component.types.indexOf("neighborhood") >= 0) {
29474 result.city = component.short_name;
29475 } else if (component.types.indexOf("locality") >= 0) {
29476 result.city = component.short_name;
29477 } else if (component.types.indexOf("sublocality") >= 0) {
29478 result.district = component.short_name;
29479 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29480 result.stateOrProvince = component.short_name;
29481 } else if (component.types.indexOf("country") >= 0) {
29482 result.country = component.short_name;
29486 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29487 result.addressLine2 = "";
29491 setZoomLevel: function(zoom)
29493 this.gMapContext.map.setZoom(zoom);
29506 this.fireEvent('show', this);
29517 this.fireEvent('hide', this);
29522 Roo.apply(Roo.bootstrap.LocationPicker, {
29524 OverlayView : function(map, options)
29526 options = options || {};
29533 * @class Roo.bootstrap.Alert
29534 * @extends Roo.bootstrap.Component
29535 * Bootstrap Alert class - shows an alert area box
29537 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29538 Enter a valid email address
29541 * @cfg {String} title The title of alert
29542 * @cfg {String} html The content of alert
29543 * @cfg {String} weight ( success | info | warning | danger )
29544 * @cfg {String} faicon font-awesomeicon
29547 * Create a new alert
29548 * @param {Object} config The config object
29552 Roo.bootstrap.Alert = function(config){
29553 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29557 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29564 getAutoCreate : function()
29573 cls : 'roo-alert-icon'
29578 cls : 'roo-alert-title',
29583 cls : 'roo-alert-text',
29590 cfg.cn[0].cls += ' fa ' + this.faicon;
29594 cfg.cls += ' alert-' + this.weight;
29600 initEvents: function()
29602 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29605 setTitle : function(str)
29607 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29610 setText : function(str)
29612 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29615 setWeight : function(weight)
29618 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29621 this.weight = weight;
29623 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29626 setIcon : function(icon)
29629 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29632 this.faicon = icon;
29634 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29655 * @class Roo.bootstrap.UploadCropbox
29656 * @extends Roo.bootstrap.Component
29657 * Bootstrap UploadCropbox class
29658 * @cfg {String} emptyText show when image has been loaded
29659 * @cfg {String} rotateNotify show when image too small to rotate
29660 * @cfg {Number} errorTimeout default 3000
29661 * @cfg {Number} minWidth default 300
29662 * @cfg {Number} minHeight default 300
29663 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29664 * @cfg {Boolean} isDocument (true|false) default false
29665 * @cfg {String} url action url
29666 * @cfg {String} paramName default 'imageUpload'
29667 * @cfg {String} method default POST
29668 * @cfg {Boolean} loadMask (true|false) default true
29669 * @cfg {Boolean} loadingText default 'Loading...'
29672 * Create a new UploadCropbox
29673 * @param {Object} config The config object
29676 Roo.bootstrap.UploadCropbox = function(config){
29677 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29681 * @event beforeselectfile
29682 * Fire before select file
29683 * @param {Roo.bootstrap.UploadCropbox} this
29685 "beforeselectfile" : true,
29688 * Fire after initEvent
29689 * @param {Roo.bootstrap.UploadCropbox} this
29694 * Fire after initEvent
29695 * @param {Roo.bootstrap.UploadCropbox} this
29696 * @param {String} data
29701 * Fire when preparing the file data
29702 * @param {Roo.bootstrap.UploadCropbox} this
29703 * @param {Object} file
29708 * Fire when get exception
29709 * @param {Roo.bootstrap.UploadCropbox} this
29710 * @param {XMLHttpRequest} xhr
29712 "exception" : true,
29714 * @event beforeloadcanvas
29715 * Fire before load the canvas
29716 * @param {Roo.bootstrap.UploadCropbox} this
29717 * @param {String} src
29719 "beforeloadcanvas" : true,
29722 * Fire when trash image
29723 * @param {Roo.bootstrap.UploadCropbox} this
29728 * Fire when download the image
29729 * @param {Roo.bootstrap.UploadCropbox} this
29733 * @event footerbuttonclick
29734 * Fire when footerbuttonclick
29735 * @param {Roo.bootstrap.UploadCropbox} this
29736 * @param {String} type
29738 "footerbuttonclick" : true,
29742 * @param {Roo.bootstrap.UploadCropbox} this
29747 * Fire when rotate the image
29748 * @param {Roo.bootstrap.UploadCropbox} this
29749 * @param {String} pos
29754 * Fire when inspect the file
29755 * @param {Roo.bootstrap.UploadCropbox} this
29756 * @param {Object} file
29761 * Fire when xhr upload the file
29762 * @param {Roo.bootstrap.UploadCropbox} this
29763 * @param {Object} data
29768 * Fire when arrange the file data
29769 * @param {Roo.bootstrap.UploadCropbox} this
29770 * @param {Object} formData
29775 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29778 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29780 emptyText : 'Click to upload image',
29781 rotateNotify : 'Image is too small to rotate',
29782 errorTimeout : 3000,
29796 cropType : 'image/jpeg',
29798 canvasLoaded : false,
29799 isDocument : false,
29801 paramName : 'imageUpload',
29803 loadingText : 'Loading...',
29806 getAutoCreate : function()
29810 cls : 'roo-upload-cropbox',
29814 cls : 'roo-upload-cropbox-selector',
29819 cls : 'roo-upload-cropbox-body',
29820 style : 'cursor:pointer',
29824 cls : 'roo-upload-cropbox-preview'
29828 cls : 'roo-upload-cropbox-thumb'
29832 cls : 'roo-upload-cropbox-empty-notify',
29833 html : this.emptyText
29837 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29838 html : this.rotateNotify
29844 cls : 'roo-upload-cropbox-footer',
29847 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29857 onRender : function(ct, position)
29859 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29861 if (this.buttons.length) {
29863 Roo.each(this.buttons, function(bb) {
29865 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29867 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29873 this.maskEl = this.el;
29877 initEvents : function()
29879 this.urlAPI = (window.createObjectURL && window) ||
29880 (window.URL && URL.revokeObjectURL && URL) ||
29881 (window.webkitURL && webkitURL);
29883 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29884 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29886 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29887 this.selectorEl.hide();
29889 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29890 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29892 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29893 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29894 this.thumbEl.hide();
29896 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29897 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29899 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29900 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29901 this.errorEl.hide();
29903 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29904 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29905 this.footerEl.hide();
29907 this.setThumbBoxSize();
29913 this.fireEvent('initial', this);
29920 window.addEventListener("resize", function() { _this.resize(); } );
29922 this.bodyEl.on('click', this.beforeSelectFile, this);
29925 this.bodyEl.on('touchstart', this.onTouchStart, this);
29926 this.bodyEl.on('touchmove', this.onTouchMove, this);
29927 this.bodyEl.on('touchend', this.onTouchEnd, this);
29931 this.bodyEl.on('mousedown', this.onMouseDown, this);
29932 this.bodyEl.on('mousemove', this.onMouseMove, this);
29933 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29934 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29935 Roo.get(document).on('mouseup', this.onMouseUp, this);
29938 this.selectorEl.on('change', this.onFileSelected, this);
29944 this.baseScale = 1;
29946 this.baseRotate = 1;
29947 this.dragable = false;
29948 this.pinching = false;
29951 this.cropData = false;
29952 this.notifyEl.dom.innerHTML = this.emptyText;
29954 this.selectorEl.dom.value = '';
29958 resize : function()
29960 if(this.fireEvent('resize', this) != false){
29961 this.setThumbBoxPosition();
29962 this.setCanvasPosition();
29966 onFooterButtonClick : function(e, el, o, type)
29969 case 'rotate-left' :
29970 this.onRotateLeft(e);
29972 case 'rotate-right' :
29973 this.onRotateRight(e);
29976 this.beforeSelectFile(e);
29991 this.fireEvent('footerbuttonclick', this, type);
29994 beforeSelectFile : function(e)
29996 e.preventDefault();
29998 if(this.fireEvent('beforeselectfile', this) != false){
29999 this.selectorEl.dom.click();
30003 onFileSelected : function(e)
30005 e.preventDefault();
30007 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30011 var file = this.selectorEl.dom.files[0];
30013 if(this.fireEvent('inspect', this, file) != false){
30014 this.prepare(file);
30019 trash : function(e)
30021 this.fireEvent('trash', this);
30024 download : function(e)
30026 this.fireEvent('download', this);
30029 loadCanvas : function(src)
30031 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30035 this.imageEl = document.createElement('img');
30039 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30041 this.imageEl.src = src;
30045 onLoadCanvas : function()
30047 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30048 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30050 this.bodyEl.un('click', this.beforeSelectFile, this);
30052 this.notifyEl.hide();
30053 this.thumbEl.show();
30054 this.footerEl.show();
30056 this.baseRotateLevel();
30058 if(this.isDocument){
30059 this.setThumbBoxSize();
30062 this.setThumbBoxPosition();
30064 this.baseScaleLevel();
30070 this.canvasLoaded = true;
30073 this.maskEl.unmask();
30078 setCanvasPosition : function()
30080 if(!this.canvasEl){
30084 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30085 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30087 this.previewEl.setLeft(pw);
30088 this.previewEl.setTop(ph);
30092 onMouseDown : function(e)
30096 this.dragable = true;
30097 this.pinching = false;
30099 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30100 this.dragable = false;
30104 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30105 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30109 onMouseMove : function(e)
30113 if(!this.canvasLoaded){
30117 if (!this.dragable){
30121 var minX = Math.ceil(this.thumbEl.getLeft(true));
30122 var minY = Math.ceil(this.thumbEl.getTop(true));
30124 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30125 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30127 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30128 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30130 x = x - this.mouseX;
30131 y = y - this.mouseY;
30133 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30134 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30136 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30137 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30139 this.previewEl.setLeft(bgX);
30140 this.previewEl.setTop(bgY);
30142 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30143 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30146 onMouseUp : function(e)
30150 this.dragable = false;
30153 onMouseWheel : function(e)
30157 this.startScale = this.scale;
30159 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30161 if(!this.zoomable()){
30162 this.scale = this.startScale;
30171 zoomable : function()
30173 var minScale = this.thumbEl.getWidth() / this.minWidth;
30175 if(this.minWidth < this.minHeight){
30176 minScale = this.thumbEl.getHeight() / this.minHeight;
30179 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30180 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30184 (this.rotate == 0 || this.rotate == 180) &&
30186 width > this.imageEl.OriginWidth ||
30187 height > this.imageEl.OriginHeight ||
30188 (width < this.minWidth && height < this.minHeight)
30196 (this.rotate == 90 || this.rotate == 270) &&
30198 width > this.imageEl.OriginWidth ||
30199 height > this.imageEl.OriginHeight ||
30200 (width < this.minHeight && height < this.minWidth)
30207 !this.isDocument &&
30208 (this.rotate == 0 || this.rotate == 180) &&
30210 width < this.minWidth ||
30211 width > this.imageEl.OriginWidth ||
30212 height < this.minHeight ||
30213 height > this.imageEl.OriginHeight
30220 !this.isDocument &&
30221 (this.rotate == 90 || this.rotate == 270) &&
30223 width < this.minHeight ||
30224 width > this.imageEl.OriginWidth ||
30225 height < this.minWidth ||
30226 height > this.imageEl.OriginHeight
30236 onRotateLeft : function(e)
30238 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30240 var minScale = this.thumbEl.getWidth() / this.minWidth;
30242 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30243 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30245 this.startScale = this.scale;
30247 while (this.getScaleLevel() < minScale){
30249 this.scale = this.scale + 1;
30251 if(!this.zoomable()){
30256 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30257 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30262 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30269 this.scale = this.startScale;
30271 this.onRotateFail();
30276 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30278 if(this.isDocument){
30279 this.setThumbBoxSize();
30280 this.setThumbBoxPosition();
30281 this.setCanvasPosition();
30286 this.fireEvent('rotate', this, 'left');
30290 onRotateRight : function(e)
30292 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30294 var minScale = this.thumbEl.getWidth() / this.minWidth;
30296 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30297 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30299 this.startScale = this.scale;
30301 while (this.getScaleLevel() < minScale){
30303 this.scale = this.scale + 1;
30305 if(!this.zoomable()){
30310 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30311 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30316 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30323 this.scale = this.startScale;
30325 this.onRotateFail();
30330 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30332 if(this.isDocument){
30333 this.setThumbBoxSize();
30334 this.setThumbBoxPosition();
30335 this.setCanvasPosition();
30340 this.fireEvent('rotate', this, 'right');
30343 onRotateFail : function()
30345 this.errorEl.show(true);
30349 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30354 this.previewEl.dom.innerHTML = '';
30356 var canvasEl = document.createElement("canvas");
30358 var contextEl = canvasEl.getContext("2d");
30360 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30361 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30362 var center = this.imageEl.OriginWidth / 2;
30364 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30365 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30366 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30367 center = this.imageEl.OriginHeight / 2;
30370 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30372 contextEl.translate(center, center);
30373 contextEl.rotate(this.rotate * Math.PI / 180);
30375 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30377 this.canvasEl = document.createElement("canvas");
30379 this.contextEl = this.canvasEl.getContext("2d");
30381 switch (this.rotate) {
30384 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30385 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30387 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30392 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30393 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30395 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30396 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);
30400 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30405 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30406 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30408 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30409 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);
30413 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);
30418 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30419 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30421 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30422 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30426 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.previewEl.appendChild(this.canvasEl);
30435 this.setCanvasPosition();
30440 if(!this.canvasLoaded){
30444 var imageCanvas = document.createElement("canvas");
30446 var imageContext = imageCanvas.getContext("2d");
30448 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30449 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30451 var center = imageCanvas.width / 2;
30453 imageContext.translate(center, center);
30455 imageContext.rotate(this.rotate * Math.PI / 180);
30457 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30459 var canvas = document.createElement("canvas");
30461 var context = canvas.getContext("2d");
30463 canvas.width = this.minWidth;
30464 canvas.height = this.minHeight;
30466 switch (this.rotate) {
30469 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30470 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30472 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30473 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30475 var targetWidth = this.minWidth - 2 * x;
30476 var targetHeight = this.minHeight - 2 * y;
30480 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30481 scale = targetWidth / width;
30484 if(x > 0 && y == 0){
30485 scale = targetHeight / height;
30488 if(x > 0 && y > 0){
30489 scale = targetWidth / width;
30491 if(width < height){
30492 scale = targetHeight / height;
30496 context.scale(scale, scale);
30498 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30499 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30501 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30502 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30504 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30509 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30510 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30512 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30513 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30515 var targetWidth = this.minWidth - 2 * x;
30516 var targetHeight = this.minHeight - 2 * y;
30520 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30521 scale = targetWidth / width;
30524 if(x > 0 && y == 0){
30525 scale = targetHeight / height;
30528 if(x > 0 && y > 0){
30529 scale = targetWidth / width;
30531 if(width < height){
30532 scale = targetHeight / height;
30536 context.scale(scale, scale);
30538 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30539 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30541 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30542 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30544 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30546 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30551 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30552 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30554 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30555 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30557 var targetWidth = this.minWidth - 2 * x;
30558 var targetHeight = this.minHeight - 2 * y;
30562 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30563 scale = targetWidth / width;
30566 if(x > 0 && y == 0){
30567 scale = targetHeight / height;
30570 if(x > 0 && y > 0){
30571 scale = targetWidth / width;
30573 if(width < height){
30574 scale = targetHeight / height;
30578 context.scale(scale, scale);
30580 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30581 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30583 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30584 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30586 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30587 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30589 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30594 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30595 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30597 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30598 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30600 var targetWidth = this.minWidth - 2 * x;
30601 var targetHeight = this.minHeight - 2 * y;
30605 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30606 scale = targetWidth / width;
30609 if(x > 0 && y == 0){
30610 scale = targetHeight / height;
30613 if(x > 0 && y > 0){
30614 scale = targetWidth / width;
30616 if(width < height){
30617 scale = targetHeight / height;
30621 context.scale(scale, scale);
30623 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30624 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30626 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30627 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30629 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30631 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30638 this.cropData = canvas.toDataURL(this.cropType);
30640 if(this.fireEvent('crop', this, this.cropData) !== false){
30641 this.process(this.file, this.cropData);
30648 setThumbBoxSize : function()
30652 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30653 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30654 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30656 this.minWidth = width;
30657 this.minHeight = height;
30659 if(this.rotate == 90 || this.rotate == 270){
30660 this.minWidth = height;
30661 this.minHeight = width;
30666 width = Math.ceil(this.minWidth * height / this.minHeight);
30668 if(this.minWidth > this.minHeight){
30670 height = Math.ceil(this.minHeight * width / this.minWidth);
30673 this.thumbEl.setStyle({
30674 width : width + 'px',
30675 height : height + 'px'
30682 setThumbBoxPosition : function()
30684 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30685 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30687 this.thumbEl.setLeft(x);
30688 this.thumbEl.setTop(y);
30692 baseRotateLevel : function()
30694 this.baseRotate = 1;
30697 typeof(this.exif) != 'undefined' &&
30698 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30699 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30701 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30704 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30708 baseScaleLevel : function()
30712 if(this.isDocument){
30714 if(this.baseRotate == 6 || this.baseRotate == 8){
30716 height = this.thumbEl.getHeight();
30717 this.baseScale = height / this.imageEl.OriginWidth;
30719 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30720 width = this.thumbEl.getWidth();
30721 this.baseScale = width / this.imageEl.OriginHeight;
30727 height = this.thumbEl.getHeight();
30728 this.baseScale = height / this.imageEl.OriginHeight;
30730 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30731 width = this.thumbEl.getWidth();
30732 this.baseScale = width / this.imageEl.OriginWidth;
30738 if(this.baseRotate == 6 || this.baseRotate == 8){
30740 width = this.thumbEl.getHeight();
30741 this.baseScale = width / this.imageEl.OriginHeight;
30743 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30744 height = this.thumbEl.getWidth();
30745 this.baseScale = height / this.imageEl.OriginHeight;
30748 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30749 height = this.thumbEl.getWidth();
30750 this.baseScale = height / this.imageEl.OriginHeight;
30752 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30753 width = this.thumbEl.getHeight();
30754 this.baseScale = width / this.imageEl.OriginWidth;
30761 width = this.thumbEl.getWidth();
30762 this.baseScale = width / this.imageEl.OriginWidth;
30764 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30765 height = this.thumbEl.getHeight();
30766 this.baseScale = height / this.imageEl.OriginHeight;
30769 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30771 height = this.thumbEl.getHeight();
30772 this.baseScale = height / this.imageEl.OriginHeight;
30774 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30775 width = this.thumbEl.getWidth();
30776 this.baseScale = width / this.imageEl.OriginWidth;
30784 getScaleLevel : function()
30786 return this.baseScale * Math.pow(1.1, this.scale);
30789 onTouchStart : function(e)
30791 if(!this.canvasLoaded){
30792 this.beforeSelectFile(e);
30796 var touches = e.browserEvent.touches;
30802 if(touches.length == 1){
30803 this.onMouseDown(e);
30807 if(touches.length != 2){
30813 for(var i = 0, finger; finger = touches[i]; i++){
30814 coords.push(finger.pageX, finger.pageY);
30817 var x = Math.pow(coords[0] - coords[2], 2);
30818 var y = Math.pow(coords[1] - coords[3], 2);
30820 this.startDistance = Math.sqrt(x + y);
30822 this.startScale = this.scale;
30824 this.pinching = true;
30825 this.dragable = false;
30829 onTouchMove : function(e)
30831 if(!this.pinching && !this.dragable){
30835 var touches = e.browserEvent.touches;
30842 this.onMouseMove(e);
30848 for(var i = 0, finger; finger = touches[i]; i++){
30849 coords.push(finger.pageX, finger.pageY);
30852 var x = Math.pow(coords[0] - coords[2], 2);
30853 var y = Math.pow(coords[1] - coords[3], 2);
30855 this.endDistance = Math.sqrt(x + y);
30857 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30859 if(!this.zoomable()){
30860 this.scale = this.startScale;
30868 onTouchEnd : function(e)
30870 this.pinching = false;
30871 this.dragable = false;
30875 process : function(file, crop)
30878 this.maskEl.mask(this.loadingText);
30881 this.xhr = new XMLHttpRequest();
30883 file.xhr = this.xhr;
30885 this.xhr.open(this.method, this.url, true);
30888 "Accept": "application/json",
30889 "Cache-Control": "no-cache",
30890 "X-Requested-With": "XMLHttpRequest"
30893 for (var headerName in headers) {
30894 var headerValue = headers[headerName];
30896 this.xhr.setRequestHeader(headerName, headerValue);
30902 this.xhr.onload = function()
30904 _this.xhrOnLoad(_this.xhr);
30907 this.xhr.onerror = function()
30909 _this.xhrOnError(_this.xhr);
30912 var formData = new FormData();
30914 formData.append('returnHTML', 'NO');
30917 formData.append('crop', crop);
30920 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30921 formData.append(this.paramName, file, file.name);
30924 if(typeof(file.filename) != 'undefined'){
30925 formData.append('filename', file.filename);
30928 if(typeof(file.mimetype) != 'undefined'){
30929 formData.append('mimetype', file.mimetype);
30932 if(this.fireEvent('arrange', this, formData) != false){
30933 this.xhr.send(formData);
30937 xhrOnLoad : function(xhr)
30940 this.maskEl.unmask();
30943 if (xhr.readyState !== 4) {
30944 this.fireEvent('exception', this, xhr);
30948 var response = Roo.decode(xhr.responseText);
30950 if(!response.success){
30951 this.fireEvent('exception', this, xhr);
30955 var response = Roo.decode(xhr.responseText);
30957 this.fireEvent('upload', this, response);
30961 xhrOnError : function()
30964 this.maskEl.unmask();
30967 Roo.log('xhr on error');
30969 var response = Roo.decode(xhr.responseText);
30975 prepare : function(file)
30978 this.maskEl.mask(this.loadingText);
30984 if(typeof(file) === 'string'){
30985 this.loadCanvas(file);
30989 if(!file || !this.urlAPI){
30994 this.cropType = file.type;
30998 if(this.fireEvent('prepare', this, this.file) != false){
31000 var reader = new FileReader();
31002 reader.onload = function (e) {
31003 if (e.target.error) {
31004 Roo.log(e.target.error);
31008 var buffer = e.target.result,
31009 dataView = new DataView(buffer),
31011 maxOffset = dataView.byteLength - 4,
31015 if (dataView.getUint16(0) === 0xffd8) {
31016 while (offset < maxOffset) {
31017 markerBytes = dataView.getUint16(offset);
31019 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31020 markerLength = dataView.getUint16(offset + 2) + 2;
31021 if (offset + markerLength > dataView.byteLength) {
31022 Roo.log('Invalid meta data: Invalid segment size.');
31026 if(markerBytes == 0xffe1){
31027 _this.parseExifData(
31034 offset += markerLength;
31044 var url = _this.urlAPI.createObjectURL(_this.file);
31046 _this.loadCanvas(url);
31051 reader.readAsArrayBuffer(this.file);
31057 parseExifData : function(dataView, offset, length)
31059 var tiffOffset = offset + 10,
31063 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31064 // No Exif data, might be XMP data instead
31068 // Check for the ASCII code for "Exif" (0x45786966):
31069 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31070 // No Exif data, might be XMP data instead
31073 if (tiffOffset + 8 > dataView.byteLength) {
31074 Roo.log('Invalid Exif data: Invalid segment size.');
31077 // Check for the two null bytes:
31078 if (dataView.getUint16(offset + 8) !== 0x0000) {
31079 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31082 // Check the byte alignment:
31083 switch (dataView.getUint16(tiffOffset)) {
31085 littleEndian = true;
31088 littleEndian = false;
31091 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31094 // Check for the TIFF tag marker (0x002A):
31095 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31096 Roo.log('Invalid Exif data: Missing TIFF marker.');
31099 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31100 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31102 this.parseExifTags(
31105 tiffOffset + dirOffset,
31110 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31115 if (dirOffset + 6 > dataView.byteLength) {
31116 Roo.log('Invalid Exif data: Invalid directory offset.');
31119 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31120 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31121 if (dirEndOffset + 4 > dataView.byteLength) {
31122 Roo.log('Invalid Exif data: Invalid directory size.');
31125 for (i = 0; i < tagsNumber; i += 1) {
31129 dirOffset + 2 + 12 * i, // tag offset
31133 // Return the offset to the next directory:
31134 return dataView.getUint32(dirEndOffset, littleEndian);
31137 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31139 var tag = dataView.getUint16(offset, littleEndian);
31141 this.exif[tag] = this.getExifValue(
31145 dataView.getUint16(offset + 2, littleEndian), // tag type
31146 dataView.getUint32(offset + 4, littleEndian), // tag length
31151 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31153 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31162 Roo.log('Invalid Exif data: Invalid tag type.');
31166 tagSize = tagType.size * length;
31167 // Determine if the value is contained in the dataOffset bytes,
31168 // or if the value at the dataOffset is a pointer to the actual data:
31169 dataOffset = tagSize > 4 ?
31170 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31171 if (dataOffset + tagSize > dataView.byteLength) {
31172 Roo.log('Invalid Exif data: Invalid data offset.');
31175 if (length === 1) {
31176 return tagType.getValue(dataView, dataOffset, littleEndian);
31179 for (i = 0; i < length; i += 1) {
31180 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31183 if (tagType.ascii) {
31185 // Concatenate the chars:
31186 for (i = 0; i < values.length; i += 1) {
31188 // Ignore the terminating NULL byte(s):
31189 if (c === '\u0000') {
31201 Roo.apply(Roo.bootstrap.UploadCropbox, {
31203 'Orientation': 0x0112
31207 1: 0, //'top-left',
31209 3: 180, //'bottom-right',
31210 // 4: 'bottom-left',
31212 6: 90, //'right-top',
31213 // 7: 'right-bottom',
31214 8: 270 //'left-bottom'
31218 // byte, 8-bit unsigned int:
31220 getValue: function (dataView, dataOffset) {
31221 return dataView.getUint8(dataOffset);
31225 // ascii, 8-bit byte:
31227 getValue: function (dataView, dataOffset) {
31228 return String.fromCharCode(dataView.getUint8(dataOffset));
31233 // short, 16 bit int:
31235 getValue: function (dataView, dataOffset, littleEndian) {
31236 return dataView.getUint16(dataOffset, littleEndian);
31240 // long, 32 bit int:
31242 getValue: function (dataView, dataOffset, littleEndian) {
31243 return dataView.getUint32(dataOffset, littleEndian);
31247 // rational = two long values, first is numerator, second is denominator:
31249 getValue: function (dataView, dataOffset, littleEndian) {
31250 return dataView.getUint32(dataOffset, littleEndian) /
31251 dataView.getUint32(dataOffset + 4, littleEndian);
31255 // slong, 32 bit signed int:
31257 getValue: function (dataView, dataOffset, littleEndian) {
31258 return dataView.getInt32(dataOffset, littleEndian);
31262 // srational, two slongs, first is numerator, second is denominator:
31264 getValue: function (dataView, dataOffset, littleEndian) {
31265 return dataView.getInt32(dataOffset, littleEndian) /
31266 dataView.getInt32(dataOffset + 4, littleEndian);
31276 cls : 'btn-group roo-upload-cropbox-rotate-left',
31277 action : 'rotate-left',
31281 cls : 'btn btn-default',
31282 html : '<i class="fa fa-undo"></i>'
31288 cls : 'btn-group roo-upload-cropbox-picture',
31289 action : 'picture',
31293 cls : 'btn btn-default',
31294 html : '<i class="fa fa-picture-o"></i>'
31300 cls : 'btn-group roo-upload-cropbox-rotate-right',
31301 action : 'rotate-right',
31305 cls : 'btn btn-default',
31306 html : '<i class="fa fa-repeat"></i>'
31314 cls : 'btn-group roo-upload-cropbox-rotate-left',
31315 action : 'rotate-left',
31319 cls : 'btn btn-default',
31320 html : '<i class="fa fa-undo"></i>'
31326 cls : 'btn-group roo-upload-cropbox-download',
31327 action : 'download',
31331 cls : 'btn btn-default',
31332 html : '<i class="fa fa-download"></i>'
31338 cls : 'btn-group roo-upload-cropbox-crop',
31343 cls : 'btn btn-default',
31344 html : '<i class="fa fa-crop"></i>'
31350 cls : 'btn-group roo-upload-cropbox-trash',
31355 cls : 'btn btn-default',
31356 html : '<i class="fa fa-trash"></i>'
31362 cls : 'btn-group roo-upload-cropbox-rotate-right',
31363 action : 'rotate-right',
31367 cls : 'btn btn-default',
31368 html : '<i class="fa fa-repeat"></i>'
31376 cls : 'btn-group roo-upload-cropbox-rotate-left',
31377 action : 'rotate-left',
31381 cls : 'btn btn-default',
31382 html : '<i class="fa fa-undo"></i>'
31388 cls : 'btn-group roo-upload-cropbox-rotate-right',
31389 action : 'rotate-right',
31393 cls : 'btn btn-default',
31394 html : '<i class="fa fa-repeat"></i>'
31407 * @class Roo.bootstrap.DocumentManager
31408 * @extends Roo.bootstrap.Component
31409 * Bootstrap DocumentManager class
31410 * @cfg {String} paramName default 'imageUpload'
31411 * @cfg {String} toolTipName default 'filename'
31412 * @cfg {String} method default POST
31413 * @cfg {String} url action url
31414 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31415 * @cfg {Boolean} multiple multiple upload default true
31416 * @cfg {Number} thumbSize default 300
31417 * @cfg {String} fieldLabel
31418 * @cfg {Number} labelWidth default 4
31419 * @cfg {String} labelAlign (left|top) default left
31420 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31421 * @cfg {Number} labellg set the width of label (1-12)
31422 * @cfg {Number} labelmd set the width of label (1-12)
31423 * @cfg {Number} labelsm set the width of label (1-12)
31424 * @cfg {Number} labelxs set the width of label (1-12)
31427 * Create a new DocumentManager
31428 * @param {Object} config The config object
31431 Roo.bootstrap.DocumentManager = function(config){
31432 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31435 this.delegates = [];
31440 * Fire when initial the DocumentManager
31441 * @param {Roo.bootstrap.DocumentManager} this
31446 * inspect selected file
31447 * @param {Roo.bootstrap.DocumentManager} this
31448 * @param {File} file
31453 * Fire when xhr load exception
31454 * @param {Roo.bootstrap.DocumentManager} this
31455 * @param {XMLHttpRequest} xhr
31457 "exception" : true,
31459 * @event afterupload
31460 * Fire when xhr load exception
31461 * @param {Roo.bootstrap.DocumentManager} this
31462 * @param {XMLHttpRequest} xhr
31464 "afterupload" : true,
31467 * prepare the form data
31468 * @param {Roo.bootstrap.DocumentManager} this
31469 * @param {Object} formData
31474 * Fire when remove the file
31475 * @param {Roo.bootstrap.DocumentManager} this
31476 * @param {Object} file
31481 * Fire after refresh the file
31482 * @param {Roo.bootstrap.DocumentManager} this
31487 * Fire after click the image
31488 * @param {Roo.bootstrap.DocumentManager} this
31489 * @param {Object} file
31494 * Fire when upload a image and editable set to true
31495 * @param {Roo.bootstrap.DocumentManager} this
31496 * @param {Object} file
31500 * @event beforeselectfile
31501 * Fire before select file
31502 * @param {Roo.bootstrap.DocumentManager} this
31504 "beforeselectfile" : true,
31507 * Fire before process file
31508 * @param {Roo.bootstrap.DocumentManager} this
31509 * @param {Object} file
31513 * @event previewrendered
31514 * Fire when preview rendered
31515 * @param {Roo.bootstrap.DocumentManager} this
31516 * @param {Object} file
31518 "previewrendered" : true,
31521 "previewResize" : true
31526 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31535 paramName : 'imageUpload',
31536 toolTipName : 'filename',
31539 labelAlign : 'left',
31549 getAutoCreate : function()
31551 var managerWidget = {
31553 cls : 'roo-document-manager',
31557 cls : 'roo-document-manager-selector',
31562 cls : 'roo-document-manager-uploader',
31566 cls : 'roo-document-manager-upload-btn',
31567 html : '<i class="fa fa-plus"></i>'
31578 cls : 'column col-md-12',
31583 if(this.fieldLabel.length){
31588 cls : 'column col-md-12',
31589 html : this.fieldLabel
31593 cls : 'column col-md-12',
31598 if(this.labelAlign == 'left'){
31603 html : this.fieldLabel
31612 if(this.labelWidth > 12){
31613 content[0].style = "width: " + this.labelWidth + 'px';
31616 if(this.labelWidth < 13 && this.labelmd == 0){
31617 this.labelmd = this.labelWidth;
31620 if(this.labellg > 0){
31621 content[0].cls += ' col-lg-' + this.labellg;
31622 content[1].cls += ' col-lg-' + (12 - this.labellg);
31625 if(this.labelmd > 0){
31626 content[0].cls += ' col-md-' + this.labelmd;
31627 content[1].cls += ' col-md-' + (12 - this.labelmd);
31630 if(this.labelsm > 0){
31631 content[0].cls += ' col-sm-' + this.labelsm;
31632 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31635 if(this.labelxs > 0){
31636 content[0].cls += ' col-xs-' + this.labelxs;
31637 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31645 cls : 'row clearfix',
31653 initEvents : function()
31655 this.managerEl = this.el.select('.roo-document-manager', true).first();
31656 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31658 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31659 this.selectorEl.hide();
31662 this.selectorEl.attr('multiple', 'multiple');
31665 this.selectorEl.on('change', this.onFileSelected, this);
31667 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31668 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31670 this.uploader.on('click', this.onUploaderClick, this);
31672 this.renderProgressDialog();
31676 window.addEventListener("resize", function() { _this.refresh(); } );
31678 this.fireEvent('initial', this);
31681 renderProgressDialog : function()
31685 this.progressDialog = new Roo.bootstrap.Modal({
31686 cls : 'roo-document-manager-progress-dialog',
31687 allow_close : false,
31698 btnclick : function() {
31699 _this.uploadCancel();
31705 this.progressDialog.render(Roo.get(document.body));
31707 this.progress = new Roo.bootstrap.Progress({
31708 cls : 'roo-document-manager-progress',
31713 this.progress.render(this.progressDialog.getChildContainer());
31715 this.progressBar = new Roo.bootstrap.ProgressBar({
31716 cls : 'roo-document-manager-progress-bar',
31719 aria_valuemax : 12,
31723 this.progressBar.render(this.progress.getChildContainer());
31726 onUploaderClick : function(e)
31728 e.preventDefault();
31730 if(this.fireEvent('beforeselectfile', this) != false){
31731 this.selectorEl.dom.click();
31736 onFileSelected : function(e)
31738 e.preventDefault();
31740 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31744 Roo.each(this.selectorEl.dom.files, function(file){
31745 if(this.fireEvent('inspect', this, file) != false){
31746 this.files.push(file);
31756 this.selectorEl.dom.value = '';
31758 if(!this.files || !this.files.length){
31762 if(this.boxes > 0 && this.files.length > this.boxes){
31763 this.files = this.files.slice(0, this.boxes);
31766 this.uploader.show();
31768 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31769 this.uploader.hide();
31778 Roo.each(this.files, function(file){
31780 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31781 var f = this.renderPreview(file);
31786 if(file.type.indexOf('image') != -1){
31787 this.delegates.push(
31789 _this.process(file);
31790 }).createDelegate(this)
31798 _this.process(file);
31799 }).createDelegate(this)
31804 this.files = files;
31806 this.delegates = this.delegates.concat(docs);
31808 if(!this.delegates.length){
31813 this.progressBar.aria_valuemax = this.delegates.length;
31820 arrange : function()
31822 if(!this.delegates.length){
31823 this.progressDialog.hide();
31828 var delegate = this.delegates.shift();
31830 this.progressDialog.show();
31832 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31834 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31839 refresh : function()
31841 this.uploader.show();
31843 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31844 this.uploader.hide();
31847 Roo.isTouch ? this.closable(false) : this.closable(true);
31849 this.fireEvent('refresh', this);
31852 onRemove : function(e, el, o)
31854 e.preventDefault();
31856 this.fireEvent('remove', this, o);
31860 remove : function(o)
31864 Roo.each(this.files, function(file){
31865 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31874 this.files = files;
31881 Roo.each(this.files, function(file){
31886 file.target.remove();
31895 onClick : function(e, el, o)
31897 e.preventDefault();
31899 this.fireEvent('click', this, o);
31903 closable : function(closable)
31905 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31907 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31919 xhrOnLoad : function(xhr)
31921 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31925 if (xhr.readyState !== 4) {
31927 this.fireEvent('exception', this, xhr);
31931 var response = Roo.decode(xhr.responseText);
31933 if(!response.success){
31935 this.fireEvent('exception', this, xhr);
31939 var file = this.renderPreview(response.data);
31941 this.files.push(file);
31945 this.fireEvent('afterupload', this, xhr);
31949 xhrOnError : function(xhr)
31951 Roo.log('xhr on error');
31953 var response = Roo.decode(xhr.responseText);
31960 process : function(file)
31962 if(this.fireEvent('process', this, file) !== false){
31963 if(this.editable && file.type.indexOf('image') != -1){
31964 this.fireEvent('edit', this, file);
31968 this.uploadStart(file, false);
31975 uploadStart : function(file, crop)
31977 this.xhr = new XMLHttpRequest();
31979 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31984 file.xhr = this.xhr;
31986 this.managerEl.createChild({
31988 cls : 'roo-document-manager-loading',
31992 tooltip : file.name,
31993 cls : 'roo-document-manager-thumb',
31994 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32000 this.xhr.open(this.method, this.url, true);
32003 "Accept": "application/json",
32004 "Cache-Control": "no-cache",
32005 "X-Requested-With": "XMLHttpRequest"
32008 for (var headerName in headers) {
32009 var headerValue = headers[headerName];
32011 this.xhr.setRequestHeader(headerName, headerValue);
32017 this.xhr.onload = function()
32019 _this.xhrOnLoad(_this.xhr);
32022 this.xhr.onerror = function()
32024 _this.xhrOnError(_this.xhr);
32027 var formData = new FormData();
32029 formData.append('returnHTML', 'NO');
32032 formData.append('crop', crop);
32035 formData.append(this.paramName, file, file.name);
32042 if(this.fireEvent('prepare', this, formData, options) != false){
32044 if(options.manually){
32048 this.xhr.send(formData);
32052 this.uploadCancel();
32055 uploadCancel : function()
32061 this.delegates = [];
32063 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32070 renderPreview : function(file)
32072 if(typeof(file.target) != 'undefined' && file.target){
32076 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32078 var previewEl = this.managerEl.createChild({
32080 cls : 'roo-document-manager-preview',
32084 tooltip : file[this.toolTipName],
32085 cls : 'roo-document-manager-thumb',
32086 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32091 html : '<i class="fa fa-times-circle"></i>'
32096 var close = previewEl.select('button.close', true).first();
32098 close.on('click', this.onRemove, this, file);
32100 file.target = previewEl;
32102 var image = previewEl.select('img', true).first();
32106 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32108 image.on('click', this.onClick, this, file);
32110 this.fireEvent('previewrendered', this, file);
32116 onPreviewLoad : function(file, image)
32118 if(typeof(file.target) == 'undefined' || !file.target){
32122 var width = image.dom.naturalWidth || image.dom.width;
32123 var height = image.dom.naturalHeight || image.dom.height;
32125 if(!this.previewResize) {
32129 if(width > height){
32130 file.target.addClass('wide');
32134 file.target.addClass('tall');
32139 uploadFromSource : function(file, crop)
32141 this.xhr = new XMLHttpRequest();
32143 this.managerEl.createChild({
32145 cls : 'roo-document-manager-loading',
32149 tooltip : file.name,
32150 cls : 'roo-document-manager-thumb',
32151 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32157 this.xhr.open(this.method, this.url, true);
32160 "Accept": "application/json",
32161 "Cache-Control": "no-cache",
32162 "X-Requested-With": "XMLHttpRequest"
32165 for (var headerName in headers) {
32166 var headerValue = headers[headerName];
32168 this.xhr.setRequestHeader(headerName, headerValue);
32174 this.xhr.onload = function()
32176 _this.xhrOnLoad(_this.xhr);
32179 this.xhr.onerror = function()
32181 _this.xhrOnError(_this.xhr);
32184 var formData = new FormData();
32186 formData.append('returnHTML', 'NO');
32188 formData.append('crop', crop);
32190 if(typeof(file.filename) != 'undefined'){
32191 formData.append('filename', file.filename);
32194 if(typeof(file.mimetype) != 'undefined'){
32195 formData.append('mimetype', file.mimetype);
32200 if(this.fireEvent('prepare', this, formData) != false){
32201 this.xhr.send(formData);
32211 * @class Roo.bootstrap.DocumentViewer
32212 * @extends Roo.bootstrap.Component
32213 * Bootstrap DocumentViewer class
32214 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32215 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32218 * Create a new DocumentViewer
32219 * @param {Object} config The config object
32222 Roo.bootstrap.DocumentViewer = function(config){
32223 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32228 * Fire after initEvent
32229 * @param {Roo.bootstrap.DocumentViewer} this
32235 * @param {Roo.bootstrap.DocumentViewer} this
32240 * Fire after download button
32241 * @param {Roo.bootstrap.DocumentViewer} this
32246 * Fire after trash button
32247 * @param {Roo.bootstrap.DocumentViewer} this
32254 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32256 showDownload : true,
32260 getAutoCreate : function()
32264 cls : 'roo-document-viewer',
32268 cls : 'roo-document-viewer-body',
32272 cls : 'roo-document-viewer-thumb',
32276 cls : 'roo-document-viewer-image'
32284 cls : 'roo-document-viewer-footer',
32287 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32291 cls : 'btn-group roo-document-viewer-download',
32295 cls : 'btn btn-default',
32296 html : '<i class="fa fa-download"></i>'
32302 cls : 'btn-group roo-document-viewer-trash',
32306 cls : 'btn btn-default',
32307 html : '<i class="fa fa-trash"></i>'
32320 initEvents : function()
32322 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32323 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32325 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32326 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32328 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32329 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32331 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32332 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32334 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32335 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32337 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32338 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32340 this.bodyEl.on('click', this.onClick, this);
32341 this.downloadBtn.on('click', this.onDownload, this);
32342 this.trashBtn.on('click', this.onTrash, this);
32344 this.downloadBtn.hide();
32345 this.trashBtn.hide();
32347 if(this.showDownload){
32348 this.downloadBtn.show();
32351 if(this.showTrash){
32352 this.trashBtn.show();
32355 if(!this.showDownload && !this.showTrash) {
32356 this.footerEl.hide();
32361 initial : function()
32363 this.fireEvent('initial', this);
32367 onClick : function(e)
32369 e.preventDefault();
32371 this.fireEvent('click', this);
32374 onDownload : function(e)
32376 e.preventDefault();
32378 this.fireEvent('download', this);
32381 onTrash : function(e)
32383 e.preventDefault();
32385 this.fireEvent('trash', this);
32397 * @class Roo.bootstrap.NavProgressBar
32398 * @extends Roo.bootstrap.Component
32399 * Bootstrap NavProgressBar class
32402 * Create a new nav progress bar
32403 * @param {Object} config The config object
32406 Roo.bootstrap.NavProgressBar = function(config){
32407 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32409 this.bullets = this.bullets || [];
32411 // Roo.bootstrap.NavProgressBar.register(this);
32415 * Fires when the active item changes
32416 * @param {Roo.bootstrap.NavProgressBar} this
32417 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32418 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32425 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32430 getAutoCreate : function()
32432 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32436 cls : 'roo-navigation-bar-group',
32440 cls : 'roo-navigation-top-bar'
32444 cls : 'roo-navigation-bullets-bar',
32448 cls : 'roo-navigation-bar'
32455 cls : 'roo-navigation-bottom-bar'
32465 initEvents: function()
32470 onRender : function(ct, position)
32472 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32474 if(this.bullets.length){
32475 Roo.each(this.bullets, function(b){
32484 addItem : function(cfg)
32486 var item = new Roo.bootstrap.NavProgressItem(cfg);
32488 item.parentId = this.id;
32489 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32492 var top = new Roo.bootstrap.Element({
32494 cls : 'roo-navigation-bar-text'
32497 var bottom = new Roo.bootstrap.Element({
32499 cls : 'roo-navigation-bar-text'
32502 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32503 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32505 var topText = new Roo.bootstrap.Element({
32507 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32510 var bottomText = new Roo.bootstrap.Element({
32512 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32515 topText.onRender(top.el, null);
32516 bottomText.onRender(bottom.el, null);
32519 item.bottomEl = bottom;
32522 this.barItems.push(item);
32527 getActive : function()
32529 var active = false;
32531 Roo.each(this.barItems, function(v){
32533 if (!v.isActive()) {
32545 setActiveItem : function(item)
32549 Roo.each(this.barItems, function(v){
32550 if (v.rid == item.rid) {
32554 if (v.isActive()) {
32555 v.setActive(false);
32560 item.setActive(true);
32562 this.fireEvent('changed', this, item, prev);
32565 getBarItem: function(rid)
32569 Roo.each(this.barItems, function(e) {
32570 if (e.rid != rid) {
32581 indexOfItem : function(item)
32585 Roo.each(this.barItems, function(v, i){
32587 if (v.rid != item.rid) {
32598 setActiveNext : function()
32600 var i = this.indexOfItem(this.getActive());
32602 if (i > this.barItems.length) {
32606 this.setActiveItem(this.barItems[i+1]);
32609 setActivePrev : function()
32611 var i = this.indexOfItem(this.getActive());
32617 this.setActiveItem(this.barItems[i-1]);
32620 format : function()
32622 if(!this.barItems.length){
32626 var width = 100 / this.barItems.length;
32628 Roo.each(this.barItems, function(i){
32629 i.el.setStyle('width', width + '%');
32630 i.topEl.el.setStyle('width', width + '%');
32631 i.bottomEl.el.setStyle('width', width + '%');
32640 * Nav Progress Item
32645 * @class Roo.bootstrap.NavProgressItem
32646 * @extends Roo.bootstrap.Component
32647 * Bootstrap NavProgressItem class
32648 * @cfg {String} rid the reference id
32649 * @cfg {Boolean} active (true|false) Is item active default false
32650 * @cfg {Boolean} disabled (true|false) Is item active default false
32651 * @cfg {String} html
32652 * @cfg {String} position (top|bottom) text position default bottom
32653 * @cfg {String} icon show icon instead of number
32656 * Create a new NavProgressItem
32657 * @param {Object} config The config object
32659 Roo.bootstrap.NavProgressItem = function(config){
32660 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32665 * The raw click event for the entire grid.
32666 * @param {Roo.bootstrap.NavProgressItem} this
32667 * @param {Roo.EventObject} e
32674 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32680 position : 'bottom',
32683 getAutoCreate : function()
32685 var iconCls = 'roo-navigation-bar-item-icon';
32687 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32691 cls: 'roo-navigation-bar-item',
32701 cfg.cls += ' active';
32704 cfg.cls += ' disabled';
32710 disable : function()
32712 this.setDisabled(true);
32715 enable : function()
32717 this.setDisabled(false);
32720 initEvents: function()
32722 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32724 this.iconEl.on('click', this.onClick, this);
32727 onClick : function(e)
32729 e.preventDefault();
32735 if(this.fireEvent('click', this, e) === false){
32739 this.parent().setActiveItem(this);
32742 isActive: function ()
32744 return this.active;
32747 setActive : function(state)
32749 if(this.active == state){
32753 this.active = state;
32756 this.el.addClass('active');
32760 this.el.removeClass('active');
32765 setDisabled : function(state)
32767 if(this.disabled == state){
32771 this.disabled = state;
32774 this.el.addClass('disabled');
32778 this.el.removeClass('disabled');
32781 tooltipEl : function()
32783 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32796 * @class Roo.bootstrap.FieldLabel
32797 * @extends Roo.bootstrap.Component
32798 * Bootstrap FieldLabel class
32799 * @cfg {String} html contents of the element
32800 * @cfg {String} tag tag of the element default label
32801 * @cfg {String} cls class of the element
32802 * @cfg {String} target label target
32803 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32804 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32805 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32806 * @cfg {String} iconTooltip default "This field is required"
32807 * @cfg {String} indicatorpos (left|right) default left
32810 * Create a new FieldLabel
32811 * @param {Object} config The config object
32814 Roo.bootstrap.FieldLabel = function(config){
32815 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32820 * Fires after the field has been marked as invalid.
32821 * @param {Roo.form.FieldLabel} this
32822 * @param {String} msg The validation message
32827 * Fires after the field has been validated with no errors.
32828 * @param {Roo.form.FieldLabel} this
32834 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32841 invalidClass : 'has-warning',
32842 validClass : 'has-success',
32843 iconTooltip : 'This field is required',
32844 indicatorpos : 'left',
32846 getAutoCreate : function(){
32849 if (!this.allowBlank) {
32855 cls : 'roo-bootstrap-field-label ' + this.cls,
32860 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32861 tooltip : this.iconTooltip
32870 if(this.indicatorpos == 'right'){
32873 cls : 'roo-bootstrap-field-label ' + this.cls,
32882 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32883 tooltip : this.iconTooltip
32892 initEvents: function()
32894 Roo.bootstrap.Element.superclass.initEvents.call(this);
32896 this.indicator = this.indicatorEl();
32898 if(this.indicator){
32899 this.indicator.removeClass('visible');
32900 this.indicator.addClass('invisible');
32903 Roo.bootstrap.FieldLabel.register(this);
32906 indicatorEl : function()
32908 var indicator = this.el.select('i.roo-required-indicator',true).first();
32919 * Mark this field as valid
32921 markValid : function()
32923 if(this.indicator){
32924 this.indicator.removeClass('visible');
32925 this.indicator.addClass('invisible');
32927 if (Roo.bootstrap.version == 3) {
32928 this.el.removeClass(this.invalidClass);
32929 this.el.addClass(this.validClass);
32931 this.el.removeClass('is-invalid');
32932 this.el.addClass('is-valid');
32936 this.fireEvent('valid', this);
32940 * Mark this field as invalid
32941 * @param {String} msg The validation message
32943 markInvalid : function(msg)
32945 if(this.indicator){
32946 this.indicator.removeClass('invisible');
32947 this.indicator.addClass('visible');
32949 if (Roo.bootstrap.version == 3) {
32950 this.el.removeClass(this.validClass);
32951 this.el.addClass(this.invalidClass);
32953 this.el.removeClass('is-valid');
32954 this.el.addClass('is-invalid');
32958 this.fireEvent('invalid', this, msg);
32964 Roo.apply(Roo.bootstrap.FieldLabel, {
32969 * register a FieldLabel Group
32970 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32972 register : function(label)
32974 if(this.groups.hasOwnProperty(label.target)){
32978 this.groups[label.target] = label;
32982 * fetch a FieldLabel Group based on the target
32983 * @param {string} target
32984 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32986 get: function(target) {
32987 if (typeof(this.groups[target]) == 'undefined') {
32991 return this.groups[target] ;
33000 * page DateSplitField.
33006 * @class Roo.bootstrap.DateSplitField
33007 * @extends Roo.bootstrap.Component
33008 * Bootstrap DateSplitField class
33009 * @cfg {string} fieldLabel - the label associated
33010 * @cfg {Number} labelWidth set the width of label (0-12)
33011 * @cfg {String} labelAlign (top|left)
33012 * @cfg {Boolean} dayAllowBlank (true|false) default false
33013 * @cfg {Boolean} monthAllowBlank (true|false) default false
33014 * @cfg {Boolean} yearAllowBlank (true|false) default false
33015 * @cfg {string} dayPlaceholder
33016 * @cfg {string} monthPlaceholder
33017 * @cfg {string} yearPlaceholder
33018 * @cfg {string} dayFormat default 'd'
33019 * @cfg {string} monthFormat default 'm'
33020 * @cfg {string} yearFormat default 'Y'
33021 * @cfg {Number} labellg set the width of label (1-12)
33022 * @cfg {Number} labelmd set the width of label (1-12)
33023 * @cfg {Number} labelsm set the width of label (1-12)
33024 * @cfg {Number} labelxs set the width of label (1-12)
33028 * Create a new DateSplitField
33029 * @param {Object} config The config object
33032 Roo.bootstrap.DateSplitField = function(config){
33033 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33039 * getting the data of years
33040 * @param {Roo.bootstrap.DateSplitField} this
33041 * @param {Object} years
33046 * getting the data of days
33047 * @param {Roo.bootstrap.DateSplitField} this
33048 * @param {Object} days
33053 * Fires after the field has been marked as invalid.
33054 * @param {Roo.form.Field} this
33055 * @param {String} msg The validation message
33060 * Fires after the field has been validated with no errors.
33061 * @param {Roo.form.Field} this
33067 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33070 labelAlign : 'top',
33072 dayAllowBlank : false,
33073 monthAllowBlank : false,
33074 yearAllowBlank : false,
33075 dayPlaceholder : '',
33076 monthPlaceholder : '',
33077 yearPlaceholder : '',
33081 isFormField : true,
33087 getAutoCreate : function()
33091 cls : 'row roo-date-split-field-group',
33096 cls : 'form-hidden-field roo-date-split-field-group-value',
33102 var labelCls = 'col-md-12';
33103 var contentCls = 'col-md-4';
33105 if(this.fieldLabel){
33109 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33113 html : this.fieldLabel
33118 if(this.labelAlign == 'left'){
33120 if(this.labelWidth > 12){
33121 label.style = "width: " + this.labelWidth + 'px';
33124 if(this.labelWidth < 13 && this.labelmd == 0){
33125 this.labelmd = this.labelWidth;
33128 if(this.labellg > 0){
33129 labelCls = ' col-lg-' + this.labellg;
33130 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33133 if(this.labelmd > 0){
33134 labelCls = ' col-md-' + this.labelmd;
33135 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33138 if(this.labelsm > 0){
33139 labelCls = ' col-sm-' + this.labelsm;
33140 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33143 if(this.labelxs > 0){
33144 labelCls = ' col-xs-' + this.labelxs;
33145 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33149 label.cls += ' ' + labelCls;
33151 cfg.cn.push(label);
33154 Roo.each(['day', 'month', 'year'], function(t){
33157 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33164 inputEl: function ()
33166 return this.el.select('.roo-date-split-field-group-value', true).first();
33169 onRender : function(ct, position)
33173 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33175 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33177 this.dayField = new Roo.bootstrap.ComboBox({
33178 allowBlank : this.dayAllowBlank,
33179 alwaysQuery : true,
33180 displayField : 'value',
33183 forceSelection : true,
33185 placeholder : this.dayPlaceholder,
33186 selectOnFocus : true,
33187 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33188 triggerAction : 'all',
33190 valueField : 'value',
33191 store : new Roo.data.SimpleStore({
33192 data : (function() {
33194 _this.fireEvent('days', _this, days);
33197 fields : [ 'value' ]
33200 select : function (_self, record, index)
33202 _this.setValue(_this.getValue());
33207 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33209 this.monthField = new Roo.bootstrap.MonthField({
33210 after : '<i class=\"fa fa-calendar\"></i>',
33211 allowBlank : this.monthAllowBlank,
33212 placeholder : this.monthPlaceholder,
33215 render : function (_self)
33217 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33218 e.preventDefault();
33222 select : function (_self, oldvalue, newvalue)
33224 _this.setValue(_this.getValue());
33229 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33231 this.yearField = new Roo.bootstrap.ComboBox({
33232 allowBlank : this.yearAllowBlank,
33233 alwaysQuery : true,
33234 displayField : 'value',
33237 forceSelection : true,
33239 placeholder : this.yearPlaceholder,
33240 selectOnFocus : true,
33241 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33242 triggerAction : 'all',
33244 valueField : 'value',
33245 store : new Roo.data.SimpleStore({
33246 data : (function() {
33248 _this.fireEvent('years', _this, years);
33251 fields : [ 'value' ]
33254 select : function (_self, record, index)
33256 _this.setValue(_this.getValue());
33261 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33264 setValue : function(v, format)
33266 this.inputEl.dom.value = v;
33268 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33270 var d = Date.parseDate(v, f);
33277 this.setDay(d.format(this.dayFormat));
33278 this.setMonth(d.format(this.monthFormat));
33279 this.setYear(d.format(this.yearFormat));
33286 setDay : function(v)
33288 this.dayField.setValue(v);
33289 this.inputEl.dom.value = this.getValue();
33294 setMonth : function(v)
33296 this.monthField.setValue(v, true);
33297 this.inputEl.dom.value = this.getValue();
33302 setYear : function(v)
33304 this.yearField.setValue(v);
33305 this.inputEl.dom.value = this.getValue();
33310 getDay : function()
33312 return this.dayField.getValue();
33315 getMonth : function()
33317 return this.monthField.getValue();
33320 getYear : function()
33322 return this.yearField.getValue();
33325 getValue : function()
33327 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33329 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33339 this.inputEl.dom.value = '';
33344 validate : function()
33346 var d = this.dayField.validate();
33347 var m = this.monthField.validate();
33348 var y = this.yearField.validate();
33353 (!this.dayAllowBlank && !d) ||
33354 (!this.monthAllowBlank && !m) ||
33355 (!this.yearAllowBlank && !y)
33360 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33369 this.markInvalid();
33374 markValid : function()
33377 var label = this.el.select('label', true).first();
33378 var icon = this.el.select('i.fa-star', true).first();
33384 this.fireEvent('valid', this);
33388 * Mark this field as invalid
33389 * @param {String} msg The validation message
33391 markInvalid : function(msg)
33394 var label = this.el.select('label', true).first();
33395 var icon = this.el.select('i.fa-star', true).first();
33397 if(label && !icon){
33398 this.el.select('.roo-date-split-field-label', true).createChild({
33400 cls : 'text-danger fa fa-lg fa-star',
33401 tooltip : 'This field is required',
33402 style : 'margin-right:5px;'
33406 this.fireEvent('invalid', this, msg);
33409 clearInvalid : function()
33411 var label = this.el.select('label', true).first();
33412 var icon = this.el.select('i.fa-star', true).first();
33418 this.fireEvent('valid', this);
33421 getName: function()
33431 * http://masonry.desandro.com
33433 * The idea is to render all the bricks based on vertical width...
33435 * The original code extends 'outlayer' - we might need to use that....
33441 * @class Roo.bootstrap.LayoutMasonry
33442 * @extends Roo.bootstrap.Component
33443 * Bootstrap Layout Masonry class
33446 * Create a new Element
33447 * @param {Object} config The config object
33450 Roo.bootstrap.LayoutMasonry = function(config){
33452 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33456 Roo.bootstrap.LayoutMasonry.register(this);
33462 * Fire after layout the items
33463 * @param {Roo.bootstrap.LayoutMasonry} this
33464 * @param {Roo.EventObject} e
33471 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33474 * @cfg {Boolean} isLayoutInstant = no animation?
33476 isLayoutInstant : false, // needed?
33479 * @cfg {Number} boxWidth width of the columns
33484 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33489 * @cfg {Number} padWidth padding below box..
33494 * @cfg {Number} gutter gutter width..
33499 * @cfg {Number} maxCols maximum number of columns
33505 * @cfg {Boolean} isAutoInitial defalut true
33507 isAutoInitial : true,
33512 * @cfg {Boolean} isHorizontal defalut false
33514 isHorizontal : false,
33516 currentSize : null,
33522 bricks: null, //CompositeElement
33526 _isLayoutInited : false,
33528 // isAlternative : false, // only use for vertical layout...
33531 * @cfg {Number} alternativePadWidth padding below box..
33533 alternativePadWidth : 50,
33535 selectedBrick : [],
33537 getAutoCreate : function(){
33539 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33543 cls: 'blog-masonary-wrapper ' + this.cls,
33545 cls : 'mas-boxes masonary'
33552 getChildContainer: function( )
33554 if (this.boxesEl) {
33555 return this.boxesEl;
33558 this.boxesEl = this.el.select('.mas-boxes').first();
33560 return this.boxesEl;
33564 initEvents : function()
33568 if(this.isAutoInitial){
33569 Roo.log('hook children rendered');
33570 this.on('childrenrendered', function() {
33571 Roo.log('children rendered');
33577 initial : function()
33579 this.selectedBrick = [];
33581 this.currentSize = this.el.getBox(true);
33583 Roo.EventManager.onWindowResize(this.resize, this);
33585 if(!this.isAutoInitial){
33593 //this.layout.defer(500,this);
33597 resize : function()
33599 var cs = this.el.getBox(true);
33602 this.currentSize.width == cs.width &&
33603 this.currentSize.x == cs.x &&
33604 this.currentSize.height == cs.height &&
33605 this.currentSize.y == cs.y
33607 Roo.log("no change in with or X or Y");
33611 this.currentSize = cs;
33617 layout : function()
33619 this._resetLayout();
33621 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33623 this.layoutItems( isInstant );
33625 this._isLayoutInited = true;
33627 this.fireEvent('layout', this);
33631 _resetLayout : function()
33633 if(this.isHorizontal){
33634 this.horizontalMeasureColumns();
33638 this.verticalMeasureColumns();
33642 verticalMeasureColumns : function()
33644 this.getContainerWidth();
33646 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33647 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33651 var boxWidth = this.boxWidth + this.padWidth;
33653 if(this.containerWidth < this.boxWidth){
33654 boxWidth = this.containerWidth
33657 var containerWidth = this.containerWidth;
33659 var cols = Math.floor(containerWidth / boxWidth);
33661 this.cols = Math.max( cols, 1 );
33663 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33665 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33667 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33669 this.colWidth = boxWidth + avail - this.padWidth;
33671 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33672 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33675 horizontalMeasureColumns : function()
33677 this.getContainerWidth();
33679 var boxWidth = this.boxWidth;
33681 if(this.containerWidth < boxWidth){
33682 boxWidth = this.containerWidth;
33685 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33687 this.el.setHeight(boxWidth);
33691 getContainerWidth : function()
33693 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33696 layoutItems : function( isInstant )
33698 Roo.log(this.bricks);
33700 var items = Roo.apply([], this.bricks);
33702 if(this.isHorizontal){
33703 this._horizontalLayoutItems( items , isInstant );
33707 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33708 // this._verticalAlternativeLayoutItems( items , isInstant );
33712 this._verticalLayoutItems( items , isInstant );
33716 _verticalLayoutItems : function ( items , isInstant)
33718 if ( !items || !items.length ) {
33723 ['xs', 'xs', 'xs', 'tall'],
33724 ['xs', 'xs', 'tall'],
33725 ['xs', 'xs', 'sm'],
33726 ['xs', 'xs', 'xs'],
33732 ['sm', 'xs', 'xs'],
33736 ['tall', 'xs', 'xs', 'xs'],
33737 ['tall', 'xs', 'xs'],
33749 Roo.each(items, function(item, k){
33751 switch (item.size) {
33752 // these layouts take up a full box,
33763 boxes.push([item]);
33786 var filterPattern = function(box, length)
33794 var pattern = box.slice(0, length);
33798 Roo.each(pattern, function(i){
33799 format.push(i.size);
33802 Roo.each(standard, function(s){
33804 if(String(s) != String(format)){
33813 if(!match && length == 1){
33818 filterPattern(box, length - 1);
33822 queue.push(pattern);
33824 box = box.slice(length, box.length);
33826 filterPattern(box, 4);
33832 Roo.each(boxes, function(box, k){
33838 if(box.length == 1){
33843 filterPattern(box, 4);
33847 this._processVerticalLayoutQueue( queue, isInstant );
33851 // _verticalAlternativeLayoutItems : function( items , isInstant )
33853 // if ( !items || !items.length ) {
33857 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33861 _horizontalLayoutItems : function ( items , isInstant)
33863 if ( !items || !items.length || items.length < 3) {
33869 var eItems = items.slice(0, 3);
33871 items = items.slice(3, items.length);
33874 ['xs', 'xs', 'xs', 'wide'],
33875 ['xs', 'xs', 'wide'],
33876 ['xs', 'xs', 'sm'],
33877 ['xs', 'xs', 'xs'],
33883 ['sm', 'xs', 'xs'],
33887 ['wide', 'xs', 'xs', 'xs'],
33888 ['wide', 'xs', 'xs'],
33901 Roo.each(items, function(item, k){
33903 switch (item.size) {
33914 boxes.push([item]);
33938 var filterPattern = function(box, length)
33946 var pattern = box.slice(0, length);
33950 Roo.each(pattern, function(i){
33951 format.push(i.size);
33954 Roo.each(standard, function(s){
33956 if(String(s) != String(format)){
33965 if(!match && length == 1){
33970 filterPattern(box, length - 1);
33974 queue.push(pattern);
33976 box = box.slice(length, box.length);
33978 filterPattern(box, 4);
33984 Roo.each(boxes, function(box, k){
33990 if(box.length == 1){
33995 filterPattern(box, 4);
34002 var pos = this.el.getBox(true);
34006 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34008 var hit_end = false;
34010 Roo.each(queue, function(box){
34014 Roo.each(box, function(b){
34016 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34026 Roo.each(box, function(b){
34028 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34031 mx = Math.max(mx, b.x);
34035 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34039 Roo.each(box, function(b){
34041 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34055 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34058 /** Sets position of item in DOM
34059 * @param {Element} item
34060 * @param {Number} x - horizontal position
34061 * @param {Number} y - vertical position
34062 * @param {Boolean} isInstant - disables transitions
34064 _processVerticalLayoutQueue : function( queue, isInstant )
34066 var pos = this.el.getBox(true);
34071 for (var i = 0; i < this.cols; i++){
34075 Roo.each(queue, function(box, k){
34077 var col = k % this.cols;
34079 Roo.each(box, function(b,kk){
34081 b.el.position('absolute');
34083 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34084 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34086 if(b.size == 'md-left' || b.size == 'md-right'){
34087 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34088 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34091 b.el.setWidth(width);
34092 b.el.setHeight(height);
34094 b.el.select('iframe',true).setSize(width,height);
34098 for (var i = 0; i < this.cols; i++){
34100 if(maxY[i] < maxY[col]){
34105 col = Math.min(col, i);
34109 x = pos.x + col * (this.colWidth + this.padWidth);
34113 var positions = [];
34115 switch (box.length){
34117 positions = this.getVerticalOneBoxColPositions(x, y, box);
34120 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34123 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34126 positions = this.getVerticalFourBoxColPositions(x, y, box);
34132 Roo.each(box, function(b,kk){
34134 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34136 var sz = b.el.getSize();
34138 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34146 for (var i = 0; i < this.cols; i++){
34147 mY = Math.max(mY, maxY[i]);
34150 this.el.setHeight(mY - pos.y);
34154 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34156 // var pos = this.el.getBox(true);
34159 // var maxX = pos.right;
34161 // var maxHeight = 0;
34163 // Roo.each(items, function(item, k){
34167 // item.el.position('absolute');
34169 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34171 // item.el.setWidth(width);
34173 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34175 // item.el.setHeight(height);
34178 // item.el.setXY([x, y], isInstant ? false : true);
34180 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34183 // y = y + height + this.alternativePadWidth;
34185 // maxHeight = maxHeight + height + this.alternativePadWidth;
34189 // this.el.setHeight(maxHeight);
34193 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34195 var pos = this.el.getBox(true);
34200 var maxX = pos.right;
34202 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34204 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34206 Roo.each(queue, function(box, k){
34208 Roo.each(box, function(b, kk){
34210 b.el.position('absolute');
34212 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34213 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34215 if(b.size == 'md-left' || b.size == 'md-right'){
34216 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34217 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34220 b.el.setWidth(width);
34221 b.el.setHeight(height);
34229 var positions = [];
34231 switch (box.length){
34233 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34236 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34239 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34242 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34248 Roo.each(box, function(b,kk){
34250 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34252 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34260 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34262 Roo.each(eItems, function(b,k){
34264 b.size = (k == 0) ? 'sm' : 'xs';
34265 b.x = (k == 0) ? 2 : 1;
34266 b.y = (k == 0) ? 2 : 1;
34268 b.el.position('absolute');
34270 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34272 b.el.setWidth(width);
34274 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34276 b.el.setHeight(height);
34280 var positions = [];
34283 x : maxX - this.unitWidth * 2 - this.gutter,
34288 x : maxX - this.unitWidth,
34289 y : minY + (this.unitWidth + this.gutter) * 2
34293 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34297 Roo.each(eItems, function(b,k){
34299 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34305 getVerticalOneBoxColPositions : function(x, y, box)
34309 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34311 if(box[0].size == 'md-left'){
34315 if(box[0].size == 'md-right'){
34320 x : x + (this.unitWidth + this.gutter) * rand,
34327 getVerticalTwoBoxColPositions : function(x, y, box)
34331 if(box[0].size == 'xs'){
34335 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34339 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34353 x : x + (this.unitWidth + this.gutter) * 2,
34354 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34361 getVerticalThreeBoxColPositions : function(x, y, box)
34365 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34373 x : x + (this.unitWidth + this.gutter) * 1,
34378 x : x + (this.unitWidth + this.gutter) * 2,
34386 if(box[0].size == 'xs' && box[1].size == 'xs'){
34395 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34399 x : x + (this.unitWidth + this.gutter) * 1,
34413 x : x + (this.unitWidth + this.gutter) * 2,
34418 x : x + (this.unitWidth + this.gutter) * 2,
34419 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34426 getVerticalFourBoxColPositions : function(x, y, box)
34430 if(box[0].size == 'xs'){
34439 y : y + (this.unitHeight + this.gutter) * 1
34444 y : y + (this.unitHeight + this.gutter) * 2
34448 x : x + (this.unitWidth + this.gutter) * 1,
34462 x : x + (this.unitWidth + this.gutter) * 2,
34467 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34468 y : y + (this.unitHeight + this.gutter) * 1
34472 x : x + (this.unitWidth + this.gutter) * 2,
34473 y : y + (this.unitWidth + this.gutter) * 2
34480 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34484 if(box[0].size == 'md-left'){
34486 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34493 if(box[0].size == 'md-right'){
34495 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34496 y : minY + (this.unitWidth + this.gutter) * 1
34502 var rand = Math.floor(Math.random() * (4 - box[0].y));
34505 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34506 y : minY + (this.unitWidth + this.gutter) * rand
34513 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34517 if(box[0].size == 'xs'){
34520 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34525 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34526 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34534 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34539 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34540 y : minY + (this.unitWidth + this.gutter) * 2
34547 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34551 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
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) * 1
34564 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34565 y : minY + (this.unitWidth + this.gutter) * 2
34572 if(box[0].size == 'xs' && box[1].size == 'xs'){
34575 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34580 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34585 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34586 y : minY + (this.unitWidth + this.gutter) * 1
34594 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34599 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34600 y : minY + (this.unitWidth + this.gutter) * 2
34604 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34605 y : minY + (this.unitWidth + this.gutter) * 2
34612 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34616 if(box[0].size == 'xs'){
34619 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34624 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34629 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),
34634 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34635 y : minY + (this.unitWidth + this.gutter) * 1
34643 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34648 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34649 y : minY + (this.unitWidth + this.gutter) * 2
34653 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34654 y : minY + (this.unitWidth + this.gutter) * 2
34658 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),
34659 y : minY + (this.unitWidth + this.gutter) * 2
34667 * remove a Masonry Brick
34668 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34670 removeBrick : function(brick_id)
34676 for (var i = 0; i<this.bricks.length; i++) {
34677 if (this.bricks[i].id == brick_id) {
34678 this.bricks.splice(i,1);
34679 this.el.dom.removeChild(Roo.get(brick_id).dom);
34686 * adds a Masonry Brick
34687 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34689 addBrick : function(cfg)
34691 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34692 //this.register(cn);
34693 cn.parentId = this.id;
34694 cn.render(this.el);
34699 * register a Masonry Brick
34700 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34703 register : function(brick)
34705 this.bricks.push(brick);
34706 brick.masonryId = this.id;
34710 * clear all the Masonry Brick
34712 clearAll : function()
34715 //this.getChildContainer().dom.innerHTML = "";
34716 this.el.dom.innerHTML = '';
34719 getSelected : function()
34721 if (!this.selectedBrick) {
34725 return this.selectedBrick;
34729 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34733 * register a Masonry Layout
34734 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34737 register : function(layout)
34739 this.groups[layout.id] = layout;
34742 * fetch a Masonry Layout based on the masonry layout ID
34743 * @param {string} the masonry layout to add
34744 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34747 get: function(layout_id) {
34748 if (typeof(this.groups[layout_id]) == 'undefined') {
34751 return this.groups[layout_id] ;
34763 * http://masonry.desandro.com
34765 * The idea is to render all the bricks based on vertical width...
34767 * The original code extends 'outlayer' - we might need to use that....
34773 * @class Roo.bootstrap.LayoutMasonryAuto
34774 * @extends Roo.bootstrap.Component
34775 * Bootstrap Layout Masonry class
34778 * Create a new Element
34779 * @param {Object} config The config object
34782 Roo.bootstrap.LayoutMasonryAuto = function(config){
34783 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34786 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34789 * @cfg {Boolean} isFitWidth - resize the width..
34791 isFitWidth : false, // options..
34793 * @cfg {Boolean} isOriginLeft = left align?
34795 isOriginLeft : true,
34797 * @cfg {Boolean} isOriginTop = top align?
34799 isOriginTop : false,
34801 * @cfg {Boolean} isLayoutInstant = no animation?
34803 isLayoutInstant : false, // needed?
34805 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34807 isResizingContainer : true,
34809 * @cfg {Number} columnWidth width of the columns
34815 * @cfg {Number} maxCols maximum number of columns
34820 * @cfg {Number} padHeight padding below box..
34826 * @cfg {Boolean} isAutoInitial defalut true
34829 isAutoInitial : true,
34835 initialColumnWidth : 0,
34836 currentSize : null,
34838 colYs : null, // array.
34845 bricks: null, //CompositeElement
34846 cols : 0, // array?
34847 // element : null, // wrapped now this.el
34848 _isLayoutInited : null,
34851 getAutoCreate : function(){
34855 cls: 'blog-masonary-wrapper ' + this.cls,
34857 cls : 'mas-boxes masonary'
34864 getChildContainer: function( )
34866 if (this.boxesEl) {
34867 return this.boxesEl;
34870 this.boxesEl = this.el.select('.mas-boxes').first();
34872 return this.boxesEl;
34876 initEvents : function()
34880 if(this.isAutoInitial){
34881 Roo.log('hook children rendered');
34882 this.on('childrenrendered', function() {
34883 Roo.log('children rendered');
34890 initial : function()
34892 this.reloadItems();
34894 this.currentSize = this.el.getBox(true);
34896 /// was window resize... - let's see if this works..
34897 Roo.EventManager.onWindowResize(this.resize, this);
34899 if(!this.isAutoInitial){
34904 this.layout.defer(500,this);
34907 reloadItems: function()
34909 this.bricks = this.el.select('.masonry-brick', true);
34911 this.bricks.each(function(b) {
34912 //Roo.log(b.getSize());
34913 if (!b.attr('originalwidth')) {
34914 b.attr('originalwidth', b.getSize().width);
34919 Roo.log(this.bricks.elements.length);
34922 resize : function()
34925 var cs = this.el.getBox(true);
34927 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34928 Roo.log("no change in with or X");
34931 this.currentSize = cs;
34935 layout : function()
34938 this._resetLayout();
34939 //this._manageStamps();
34941 // don't animate first layout
34942 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34943 this.layoutItems( isInstant );
34945 // flag for initalized
34946 this._isLayoutInited = true;
34949 layoutItems : function( isInstant )
34951 //var items = this._getItemsForLayout( this.items );
34952 // original code supports filtering layout items.. we just ignore it..
34954 this._layoutItems( this.bricks , isInstant );
34956 this._postLayout();
34958 _layoutItems : function ( items , isInstant)
34960 //this.fireEvent( 'layout', this, items );
34963 if ( !items || !items.elements.length ) {
34964 // no items, emit event with empty array
34969 items.each(function(item) {
34970 Roo.log("layout item");
34972 // get x/y object from method
34973 var position = this._getItemLayoutPosition( item );
34975 position.item = item;
34976 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34977 queue.push( position );
34980 this._processLayoutQueue( queue );
34982 /** Sets position of item in DOM
34983 * @param {Element} item
34984 * @param {Number} x - horizontal position
34985 * @param {Number} y - vertical position
34986 * @param {Boolean} isInstant - disables transitions
34988 _processLayoutQueue : function( queue )
34990 for ( var i=0, len = queue.length; i < len; i++ ) {
34991 var obj = queue[i];
34992 obj.item.position('absolute');
34993 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34999 * Any logic you want to do after each layout,
35000 * i.e. size the container
35002 _postLayout : function()
35004 this.resizeContainer();
35007 resizeContainer : function()
35009 if ( !this.isResizingContainer ) {
35012 var size = this._getContainerSize();
35014 this.el.setSize(size.width,size.height);
35015 this.boxesEl.setSize(size.width,size.height);
35021 _resetLayout : function()
35023 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35024 this.colWidth = this.el.getWidth();
35025 //this.gutter = this.el.getWidth();
35027 this.measureColumns();
35033 this.colYs.push( 0 );
35039 measureColumns : function()
35041 this.getContainerWidth();
35042 // if columnWidth is 0, default to outerWidth of first item
35043 if ( !this.columnWidth ) {
35044 var firstItem = this.bricks.first();
35045 Roo.log(firstItem);
35046 this.columnWidth = this.containerWidth;
35047 if (firstItem && firstItem.attr('originalwidth') ) {
35048 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35050 // columnWidth fall back to item of first element
35051 Roo.log("set column width?");
35052 this.initialColumnWidth = this.columnWidth ;
35054 // if first elem has no width, default to size of container
35059 if (this.initialColumnWidth) {
35060 this.columnWidth = this.initialColumnWidth;
35065 // column width is fixed at the top - however if container width get's smaller we should
35068 // this bit calcs how man columns..
35070 var columnWidth = this.columnWidth += this.gutter;
35072 // calculate columns
35073 var containerWidth = this.containerWidth + this.gutter;
35075 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35076 // fix rounding errors, typically with gutters
35077 var excess = columnWidth - containerWidth % columnWidth;
35080 // if overshoot is less than a pixel, round up, otherwise floor it
35081 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35082 cols = Math[ mathMethod ]( cols );
35083 this.cols = Math.max( cols, 1 );
35084 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35086 // padding positioning..
35087 var totalColWidth = this.cols * this.columnWidth;
35088 var padavail = this.containerWidth - totalColWidth;
35089 // so for 2 columns - we need 3 'pads'
35091 var padNeeded = (1+this.cols) * this.padWidth;
35093 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35095 this.columnWidth += padExtra
35096 //this.padWidth = Math.floor(padavail / ( this.cols));
35098 // adjust colum width so that padding is fixed??
35100 // we have 3 columns ... total = width * 3
35101 // we have X left over... that should be used by
35103 //if (this.expandC) {
35111 getContainerWidth : function()
35113 /* // container is parent if fit width
35114 var container = this.isFitWidth ? this.element.parentNode : this.element;
35115 // check that this.size and size are there
35116 // IE8 triggers resize on body size change, so they might not be
35118 var size = getSize( container ); //FIXME
35119 this.containerWidth = size && size.innerWidth; //FIXME
35122 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35126 _getItemLayoutPosition : function( item ) // what is item?
35128 // we resize the item to our columnWidth..
35130 item.setWidth(this.columnWidth);
35131 item.autoBoxAdjust = false;
35133 var sz = item.getSize();
35135 // how many columns does this brick span
35136 var remainder = this.containerWidth % this.columnWidth;
35138 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35139 // round if off by 1 pixel, otherwise use ceil
35140 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35141 colSpan = Math.min( colSpan, this.cols );
35143 // normally this should be '1' as we dont' currently allow multi width columns..
35145 var colGroup = this._getColGroup( colSpan );
35146 // get the minimum Y value from the columns
35147 var minimumY = Math.min.apply( Math, colGroup );
35148 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35150 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35152 // position the brick
35154 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35155 y: this.currentSize.y + minimumY + this.padHeight
35159 // apply setHeight to necessary columns
35160 var setHeight = minimumY + sz.height + this.padHeight;
35161 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35163 var setSpan = this.cols + 1 - colGroup.length;
35164 for ( var i = 0; i < setSpan; i++ ) {
35165 this.colYs[ shortColIndex + i ] = setHeight ;
35172 * @param {Number} colSpan - number of columns the element spans
35173 * @returns {Array} colGroup
35175 _getColGroup : function( colSpan )
35177 if ( colSpan < 2 ) {
35178 // if brick spans only one column, use all the column Ys
35183 // how many different places could this brick fit horizontally
35184 var groupCount = this.cols + 1 - colSpan;
35185 // for each group potential horizontal position
35186 for ( var i = 0; i < groupCount; i++ ) {
35187 // make an array of colY values for that one group
35188 var groupColYs = this.colYs.slice( i, i + colSpan );
35189 // and get the max value of the array
35190 colGroup[i] = Math.max.apply( Math, groupColYs );
35195 _manageStamp : function( stamp )
35197 var stampSize = stamp.getSize();
35198 var offset = stamp.getBox();
35199 // get the columns that this stamp affects
35200 var firstX = this.isOriginLeft ? offset.x : offset.right;
35201 var lastX = firstX + stampSize.width;
35202 var firstCol = Math.floor( firstX / this.columnWidth );
35203 firstCol = Math.max( 0, firstCol );
35205 var lastCol = Math.floor( lastX / this.columnWidth );
35206 // lastCol should not go over if multiple of columnWidth #425
35207 lastCol -= lastX % this.columnWidth ? 0 : 1;
35208 lastCol = Math.min( this.cols - 1, lastCol );
35210 // set colYs to bottom of the stamp
35211 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35214 for ( var i = firstCol; i <= lastCol; i++ ) {
35215 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35220 _getContainerSize : function()
35222 this.maxY = Math.max.apply( Math, this.colYs );
35227 if ( this.isFitWidth ) {
35228 size.width = this._getContainerFitWidth();
35234 _getContainerFitWidth : function()
35236 var unusedCols = 0;
35237 // count unused columns
35240 if ( this.colYs[i] !== 0 ) {
35245 // fit container to columns that have been used
35246 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35249 needsResizeLayout : function()
35251 var previousWidth = this.containerWidth;
35252 this.getContainerWidth();
35253 return previousWidth !== this.containerWidth;
35268 * @class Roo.bootstrap.MasonryBrick
35269 * @extends Roo.bootstrap.Component
35270 * Bootstrap MasonryBrick class
35273 * Create a new MasonryBrick
35274 * @param {Object} config The config object
35277 Roo.bootstrap.MasonryBrick = function(config){
35279 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35281 Roo.bootstrap.MasonryBrick.register(this);
35287 * When a MasonryBrick is clcik
35288 * @param {Roo.bootstrap.MasonryBrick} this
35289 * @param {Roo.EventObject} e
35295 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35298 * @cfg {String} title
35302 * @cfg {String} html
35306 * @cfg {String} bgimage
35310 * @cfg {String} videourl
35314 * @cfg {String} cls
35318 * @cfg {String} href
35322 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35327 * @cfg {String} placetitle (center|bottom)
35332 * @cfg {Boolean} isFitContainer defalut true
35334 isFitContainer : true,
35337 * @cfg {Boolean} preventDefault defalut false
35339 preventDefault : false,
35342 * @cfg {Boolean} inverse defalut false
35344 maskInverse : false,
35346 getAutoCreate : function()
35348 if(!this.isFitContainer){
35349 return this.getSplitAutoCreate();
35352 var cls = 'masonry-brick masonry-brick-full';
35354 if(this.href.length){
35355 cls += ' masonry-brick-link';
35358 if(this.bgimage.length){
35359 cls += ' masonry-brick-image';
35362 if(this.maskInverse){
35363 cls += ' mask-inverse';
35366 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35367 cls += ' enable-mask';
35371 cls += ' masonry-' + this.size + '-brick';
35374 if(this.placetitle.length){
35376 switch (this.placetitle) {
35378 cls += ' masonry-center-title';
35381 cls += ' masonry-bottom-title';
35388 if(!this.html.length && !this.bgimage.length){
35389 cls += ' masonry-center-title';
35392 if(!this.html.length && this.bgimage.length){
35393 cls += ' masonry-bottom-title';
35398 cls += ' ' + this.cls;
35402 tag: (this.href.length) ? 'a' : 'div',
35407 cls: 'masonry-brick-mask'
35411 cls: 'masonry-brick-paragraph',
35417 if(this.href.length){
35418 cfg.href = this.href;
35421 var cn = cfg.cn[1].cn;
35423 if(this.title.length){
35426 cls: 'masonry-brick-title',
35431 if(this.html.length){
35434 cls: 'masonry-brick-text',
35439 if (!this.title.length && !this.html.length) {
35440 cfg.cn[1].cls += ' hide';
35443 if(this.bgimage.length){
35446 cls: 'masonry-brick-image-view',
35451 if(this.videourl.length){
35452 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35453 // youtube support only?
35456 cls: 'masonry-brick-image-view',
35459 allowfullscreen : true
35467 getSplitAutoCreate : function()
35469 var cls = 'masonry-brick masonry-brick-split';
35471 if(this.href.length){
35472 cls += ' masonry-brick-link';
35475 if(this.bgimage.length){
35476 cls += ' masonry-brick-image';
35480 cls += ' masonry-' + this.size + '-brick';
35483 switch (this.placetitle) {
35485 cls += ' masonry-center-title';
35488 cls += ' masonry-bottom-title';
35491 if(!this.bgimage.length){
35492 cls += ' masonry-center-title';
35495 if(this.bgimage.length){
35496 cls += ' masonry-bottom-title';
35502 cls += ' ' + this.cls;
35506 tag: (this.href.length) ? 'a' : 'div',
35511 cls: 'masonry-brick-split-head',
35515 cls: 'masonry-brick-paragraph',
35522 cls: 'masonry-brick-split-body',
35528 if(this.href.length){
35529 cfg.href = this.href;
35532 if(this.title.length){
35533 cfg.cn[0].cn[0].cn.push({
35535 cls: 'masonry-brick-title',
35540 if(this.html.length){
35541 cfg.cn[1].cn.push({
35543 cls: 'masonry-brick-text',
35548 if(this.bgimage.length){
35549 cfg.cn[0].cn.push({
35551 cls: 'masonry-brick-image-view',
35556 if(this.videourl.length){
35557 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35558 // youtube support only?
35559 cfg.cn[0].cn.cn.push({
35561 cls: 'masonry-brick-image-view',
35564 allowfullscreen : true
35571 initEvents: function()
35573 switch (this.size) {
35606 this.el.on('touchstart', this.onTouchStart, this);
35607 this.el.on('touchmove', this.onTouchMove, this);
35608 this.el.on('touchend', this.onTouchEnd, this);
35609 this.el.on('contextmenu', this.onContextMenu, this);
35611 this.el.on('mouseenter' ,this.enter, this);
35612 this.el.on('mouseleave', this.leave, this);
35613 this.el.on('click', this.onClick, this);
35616 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35617 this.parent().bricks.push(this);
35622 onClick: function(e, el)
35624 var time = this.endTimer - this.startTimer;
35625 // Roo.log(e.preventDefault());
35628 e.preventDefault();
35633 if(!this.preventDefault){
35637 e.preventDefault();
35639 if (this.activeClass != '') {
35640 this.selectBrick();
35643 this.fireEvent('click', this, e);
35646 enter: function(e, el)
35648 e.preventDefault();
35650 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35654 if(this.bgimage.length && this.html.length){
35655 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35659 leave: function(e, el)
35661 e.preventDefault();
35663 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35667 if(this.bgimage.length && this.html.length){
35668 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35672 onTouchStart: function(e, el)
35674 // e.preventDefault();
35676 this.touchmoved = false;
35678 if(!this.isFitContainer){
35682 if(!this.bgimage.length || !this.html.length){
35686 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35688 this.timer = new Date().getTime();
35692 onTouchMove: function(e, el)
35694 this.touchmoved = true;
35697 onContextMenu : function(e,el)
35699 e.preventDefault();
35700 e.stopPropagation();
35704 onTouchEnd: function(e, el)
35706 // e.preventDefault();
35708 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35715 if(!this.bgimage.length || !this.html.length){
35717 if(this.href.length){
35718 window.location.href = this.href;
35724 if(!this.isFitContainer){
35728 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35730 window.location.href = this.href;
35733 //selection on single brick only
35734 selectBrick : function() {
35736 if (!this.parentId) {
35740 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35741 var index = m.selectedBrick.indexOf(this.id);
35744 m.selectedBrick.splice(index,1);
35745 this.el.removeClass(this.activeClass);
35749 for(var i = 0; i < m.selectedBrick.length; i++) {
35750 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35751 b.el.removeClass(b.activeClass);
35754 m.selectedBrick = [];
35756 m.selectedBrick.push(this.id);
35757 this.el.addClass(this.activeClass);
35761 isSelected : function(){
35762 return this.el.hasClass(this.activeClass);
35767 Roo.apply(Roo.bootstrap.MasonryBrick, {
35770 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35772 * register a Masonry Brick
35773 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35776 register : function(brick)
35778 //this.groups[brick.id] = brick;
35779 this.groups.add(brick.id, brick);
35782 * fetch a masonry brick based on the masonry brick ID
35783 * @param {string} the masonry brick to add
35784 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35787 get: function(brick_id)
35789 // if (typeof(this.groups[brick_id]) == 'undefined') {
35792 // return this.groups[brick_id] ;
35794 if(this.groups.key(brick_id)) {
35795 return this.groups.key(brick_id);
35813 * @class Roo.bootstrap.Brick
35814 * @extends Roo.bootstrap.Component
35815 * Bootstrap Brick class
35818 * Create a new Brick
35819 * @param {Object} config The config object
35822 Roo.bootstrap.Brick = function(config){
35823 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35829 * When a Brick is click
35830 * @param {Roo.bootstrap.Brick} this
35831 * @param {Roo.EventObject} e
35837 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35840 * @cfg {String} title
35844 * @cfg {String} html
35848 * @cfg {String} bgimage
35852 * @cfg {String} cls
35856 * @cfg {String} href
35860 * @cfg {String} video
35864 * @cfg {Boolean} square
35868 getAutoCreate : function()
35870 var cls = 'roo-brick';
35872 if(this.href.length){
35873 cls += ' roo-brick-link';
35876 if(this.bgimage.length){
35877 cls += ' roo-brick-image';
35880 if(!this.html.length && !this.bgimage.length){
35881 cls += ' roo-brick-center-title';
35884 if(!this.html.length && this.bgimage.length){
35885 cls += ' roo-brick-bottom-title';
35889 cls += ' ' + this.cls;
35893 tag: (this.href.length) ? 'a' : 'div',
35898 cls: 'roo-brick-paragraph',
35904 if(this.href.length){
35905 cfg.href = this.href;
35908 var cn = cfg.cn[0].cn;
35910 if(this.title.length){
35913 cls: 'roo-brick-title',
35918 if(this.html.length){
35921 cls: 'roo-brick-text',
35928 if(this.bgimage.length){
35931 cls: 'roo-brick-image-view',
35939 initEvents: function()
35941 if(this.title.length || this.html.length){
35942 this.el.on('mouseenter' ,this.enter, this);
35943 this.el.on('mouseleave', this.leave, this);
35946 Roo.EventManager.onWindowResize(this.resize, this);
35948 if(this.bgimage.length){
35949 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35950 this.imageEl.on('load', this.onImageLoad, this);
35957 onImageLoad : function()
35962 resize : function()
35964 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35966 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35968 if(this.bgimage.length){
35969 var image = this.el.select('.roo-brick-image-view', true).first();
35971 image.setWidth(paragraph.getWidth());
35974 image.setHeight(paragraph.getWidth());
35977 this.el.setHeight(image.getHeight());
35978 paragraph.setHeight(image.getHeight());
35984 enter: function(e, el)
35986 e.preventDefault();
35988 if(this.bgimage.length){
35989 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35990 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35994 leave: function(e, el)
35996 e.preventDefault();
35998 if(this.bgimage.length){
35999 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36000 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36015 * @class Roo.bootstrap.NumberField
36016 * @extends Roo.bootstrap.Input
36017 * Bootstrap NumberField class
36023 * Create a new NumberField
36024 * @param {Object} config The config object
36027 Roo.bootstrap.NumberField = function(config){
36028 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36031 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36034 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36036 allowDecimals : true,
36038 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36040 decimalSeparator : ".",
36042 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36044 decimalPrecision : 2,
36046 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36048 allowNegative : true,
36051 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36055 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36057 minValue : Number.NEGATIVE_INFINITY,
36059 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36061 maxValue : Number.MAX_VALUE,
36063 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36065 minText : "The minimum value for this field is {0}",
36067 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36069 maxText : "The maximum value for this field is {0}",
36071 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36072 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36074 nanText : "{0} is not a valid number",
36076 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36078 thousandsDelimiter : false,
36080 * @cfg {String} valueAlign alignment of value
36082 valueAlign : "left",
36084 getAutoCreate : function()
36086 var hiddenInput = {
36090 cls: 'hidden-number-input'
36094 hiddenInput.name = this.name;
36099 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36101 this.name = hiddenInput.name;
36103 if(cfg.cn.length > 0) {
36104 cfg.cn.push(hiddenInput);
36111 initEvents : function()
36113 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36115 var allowed = "0123456789";
36117 if(this.allowDecimals){
36118 allowed += this.decimalSeparator;
36121 if(this.allowNegative){
36125 if(this.thousandsDelimiter) {
36129 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36131 var keyPress = function(e){
36133 var k = e.getKey();
36135 var c = e.getCharCode();
36138 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36139 allowed.indexOf(String.fromCharCode(c)) === -1
36145 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36149 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36154 this.el.on("keypress", keyPress, this);
36157 validateValue : function(value)
36160 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36164 var num = this.parseValue(value);
36167 this.markInvalid(String.format(this.nanText, value));
36171 if(num < this.minValue){
36172 this.markInvalid(String.format(this.minText, this.minValue));
36176 if(num > this.maxValue){
36177 this.markInvalid(String.format(this.maxText, this.maxValue));
36184 getValue : function()
36186 var v = this.hiddenEl().getValue();
36188 return this.fixPrecision(this.parseValue(v));
36191 parseValue : function(value)
36193 if(this.thousandsDelimiter) {
36195 r = new RegExp(",", "g");
36196 value = value.replace(r, "");
36199 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36200 return isNaN(value) ? '' : value;
36203 fixPrecision : function(value)
36205 if(this.thousandsDelimiter) {
36207 r = new RegExp(",", "g");
36208 value = value.replace(r, "");
36211 var nan = isNaN(value);
36213 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36214 return nan ? '' : value;
36216 return parseFloat(value).toFixed(this.decimalPrecision);
36219 setValue : function(v)
36221 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36227 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36229 this.inputEl().dom.value = (v == '') ? '' :
36230 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36232 if(!this.allowZero && v === '0') {
36233 this.hiddenEl().dom.value = '';
36234 this.inputEl().dom.value = '';
36241 decimalPrecisionFcn : function(v)
36243 return Math.floor(v);
36246 beforeBlur : function()
36248 var v = this.parseValue(this.getRawValue());
36250 if(v || v === 0 || v === ''){
36255 hiddenEl : function()
36257 return this.el.select('input.hidden-number-input',true).first();
36269 * @class Roo.bootstrap.DocumentSlider
36270 * @extends Roo.bootstrap.Component
36271 * Bootstrap DocumentSlider class
36274 * Create a new DocumentViewer
36275 * @param {Object} config The config object
36278 Roo.bootstrap.DocumentSlider = function(config){
36279 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36286 * Fire after initEvent
36287 * @param {Roo.bootstrap.DocumentSlider} this
36292 * Fire after update
36293 * @param {Roo.bootstrap.DocumentSlider} this
36299 * @param {Roo.bootstrap.DocumentSlider} this
36305 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36311 getAutoCreate : function()
36315 cls : 'roo-document-slider',
36319 cls : 'roo-document-slider-header',
36323 cls : 'roo-document-slider-header-title'
36329 cls : 'roo-document-slider-body',
36333 cls : 'roo-document-slider-prev',
36337 cls : 'fa fa-chevron-left'
36343 cls : 'roo-document-slider-thumb',
36347 cls : 'roo-document-slider-image'
36353 cls : 'roo-document-slider-next',
36357 cls : 'fa fa-chevron-right'
36369 initEvents : function()
36371 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36372 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36374 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36375 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36377 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36378 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36380 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36381 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36383 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36384 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36386 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36387 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36389 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36390 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36392 this.thumbEl.on('click', this.onClick, this);
36394 this.prevIndicator.on('click', this.prev, this);
36396 this.nextIndicator.on('click', this.next, this);
36400 initial : function()
36402 if(this.files.length){
36403 this.indicator = 1;
36407 this.fireEvent('initial', this);
36410 update : function()
36412 this.imageEl.attr('src', this.files[this.indicator - 1]);
36414 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36416 this.prevIndicator.show();
36418 if(this.indicator == 1){
36419 this.prevIndicator.hide();
36422 this.nextIndicator.show();
36424 if(this.indicator == this.files.length){
36425 this.nextIndicator.hide();
36428 this.thumbEl.scrollTo('top');
36430 this.fireEvent('update', this);
36433 onClick : function(e)
36435 e.preventDefault();
36437 this.fireEvent('click', this);
36442 e.preventDefault();
36444 this.indicator = Math.max(1, this.indicator - 1);
36451 e.preventDefault();
36453 this.indicator = Math.min(this.files.length, this.indicator + 1);
36467 * @class Roo.bootstrap.RadioSet
36468 * @extends Roo.bootstrap.Input
36469 * Bootstrap RadioSet class
36470 * @cfg {String} indicatorpos (left|right) default left
36471 * @cfg {Boolean} inline (true|false) inline the element (default true)
36472 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36474 * Create a new RadioSet
36475 * @param {Object} config The config object
36478 Roo.bootstrap.RadioSet = function(config){
36480 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36484 Roo.bootstrap.RadioSet.register(this);
36489 * Fires when the element is checked or unchecked.
36490 * @param {Roo.bootstrap.RadioSet} this This radio
36491 * @param {Roo.bootstrap.Radio} item The checked item
36496 * Fires when the element is click.
36497 * @param {Roo.bootstrap.RadioSet} this This radio set
36498 * @param {Roo.bootstrap.Radio} item The checked item
36499 * @param {Roo.EventObject} e The event object
36506 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36514 indicatorpos : 'left',
36516 getAutoCreate : function()
36520 cls : 'roo-radio-set-label',
36524 html : this.fieldLabel
36528 if (Roo.bootstrap.version == 3) {
36531 if(this.indicatorpos == 'left'){
36534 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36535 tooltip : 'This field is required'
36540 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36541 tooltip : 'This field is required'
36547 cls : 'roo-radio-set-items'
36550 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36552 if (align === 'left' && this.fieldLabel.length) {
36555 cls : "roo-radio-set-right",
36561 if(this.labelWidth > 12){
36562 label.style = "width: " + this.labelWidth + 'px';
36565 if(this.labelWidth < 13 && this.labelmd == 0){
36566 this.labelmd = this.labelWidth;
36569 if(this.labellg > 0){
36570 label.cls += ' col-lg-' + this.labellg;
36571 items.cls += ' col-lg-' + (12 - this.labellg);
36574 if(this.labelmd > 0){
36575 label.cls += ' col-md-' + this.labelmd;
36576 items.cls += ' col-md-' + (12 - this.labelmd);
36579 if(this.labelsm > 0){
36580 label.cls += ' col-sm-' + this.labelsm;
36581 items.cls += ' col-sm-' + (12 - this.labelsm);
36584 if(this.labelxs > 0){
36585 label.cls += ' col-xs-' + this.labelxs;
36586 items.cls += ' col-xs-' + (12 - this.labelxs);
36592 cls : 'roo-radio-set',
36596 cls : 'roo-radio-set-input',
36599 value : this.value ? this.value : ''
36606 if(this.weight.length){
36607 cfg.cls += ' roo-radio-' + this.weight;
36611 cfg.cls += ' roo-radio-set-inline';
36615 ['xs','sm','md','lg'].map(function(size){
36616 if (settings[size]) {
36617 cfg.cls += ' col-' + size + '-' + settings[size];
36625 initEvents : function()
36627 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36628 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36630 if(!this.fieldLabel.length){
36631 this.labelEl.hide();
36634 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36635 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36637 this.indicator = this.indicatorEl();
36639 if(this.indicator){
36640 this.indicator.addClass('invisible');
36643 this.originalValue = this.getValue();
36647 inputEl: function ()
36649 return this.el.select('.roo-radio-set-input', true).first();
36652 getChildContainer : function()
36654 return this.itemsEl;
36657 register : function(item)
36659 this.radioes.push(item);
36663 validate : function()
36665 if(this.getVisibilityEl().hasClass('hidden')){
36671 Roo.each(this.radioes, function(i){
36680 if(this.allowBlank) {
36684 if(this.disabled || valid){
36689 this.markInvalid();
36694 markValid : function()
36696 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36697 this.indicatorEl().removeClass('visible');
36698 this.indicatorEl().addClass('invisible');
36702 if (Roo.bootstrap.version == 3) {
36703 this.el.removeClass([this.invalidClass, this.validClass]);
36704 this.el.addClass(this.validClass);
36706 this.el.removeClass(['is-invalid','is-valid']);
36707 this.el.addClass(['is-valid']);
36709 this.fireEvent('valid', this);
36712 markInvalid : function(msg)
36714 if(this.allowBlank || this.disabled){
36718 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36719 this.indicatorEl().removeClass('invisible');
36720 this.indicatorEl().addClass('visible');
36722 if (Roo.bootstrap.version == 3) {
36723 this.el.removeClass([this.invalidClass, this.validClass]);
36724 this.el.addClass(this.invalidClass);
36726 this.el.removeClass(['is-invalid','is-valid']);
36727 this.el.addClass(['is-invalid']);
36730 this.fireEvent('invalid', this, msg);
36734 setValue : function(v, suppressEvent)
36736 if(this.value === v){
36743 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36746 Roo.each(this.radioes, function(i){
36748 i.el.removeClass('checked');
36751 Roo.each(this.radioes, function(i){
36753 if(i.value === v || i.value.toString() === v.toString()){
36755 i.el.addClass('checked');
36757 if(suppressEvent !== true){
36758 this.fireEvent('check', this, i);
36769 clearInvalid : function(){
36771 if(!this.el || this.preventMark){
36775 this.el.removeClass([this.invalidClass]);
36777 this.fireEvent('valid', this);
36782 Roo.apply(Roo.bootstrap.RadioSet, {
36786 register : function(set)
36788 this.groups[set.name] = set;
36791 get: function(name)
36793 if (typeof(this.groups[name]) == 'undefined') {
36797 return this.groups[name] ;
36803 * Ext JS Library 1.1.1
36804 * Copyright(c) 2006-2007, Ext JS, LLC.
36806 * Originally Released Under LGPL - original licence link has changed is not relivant.
36809 * <script type="text/javascript">
36814 * @class Roo.bootstrap.SplitBar
36815 * @extends Roo.util.Observable
36816 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36820 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36821 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36822 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36823 split.minSize = 100;
36824 split.maxSize = 600;
36825 split.animate = true;
36826 split.on('moved', splitterMoved);
36829 * Create a new SplitBar
36830 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36831 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36832 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36833 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36834 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36835 position of the SplitBar).
36837 Roo.bootstrap.SplitBar = function(cfg){
36842 // dragElement : elm
36843 // resizingElement: el,
36845 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36846 // placement : Roo.bootstrap.SplitBar.LEFT ,
36847 // existingProxy ???
36850 this.el = Roo.get(cfg.dragElement, true);
36851 this.el.dom.unselectable = "on";
36853 this.resizingEl = Roo.get(cfg.resizingElement, true);
36857 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36858 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36861 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36864 * The minimum size of the resizing element. (Defaults to 0)
36870 * The maximum size of the resizing element. (Defaults to 2000)
36873 this.maxSize = 2000;
36876 * Whether to animate the transition to the new size
36879 this.animate = false;
36882 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36885 this.useShim = false;
36890 if(!cfg.existingProxy){
36892 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36894 this.proxy = Roo.get(cfg.existingProxy).dom;
36897 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36900 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36903 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36906 this.dragSpecs = {};
36909 * @private The adapter to use to positon and resize elements
36911 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36912 this.adapter.init(this);
36914 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36916 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36917 this.el.addClass("roo-splitbar-h");
36920 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36921 this.el.addClass("roo-splitbar-v");
36927 * Fires when the splitter is moved (alias for {@link #event-moved})
36928 * @param {Roo.bootstrap.SplitBar} this
36929 * @param {Number} newSize the new width or height
36934 * Fires when the splitter is moved
36935 * @param {Roo.bootstrap.SplitBar} this
36936 * @param {Number} newSize the new width or height
36940 * @event beforeresize
36941 * Fires before the splitter is dragged
36942 * @param {Roo.bootstrap.SplitBar} this
36944 "beforeresize" : true,
36946 "beforeapply" : true
36949 Roo.util.Observable.call(this);
36952 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36953 onStartProxyDrag : function(x, y){
36954 this.fireEvent("beforeresize", this);
36956 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36958 o.enableDisplayMode("block");
36959 // all splitbars share the same overlay
36960 Roo.bootstrap.SplitBar.prototype.overlay = o;
36962 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36963 this.overlay.show();
36964 Roo.get(this.proxy).setDisplayed("block");
36965 var size = this.adapter.getElementSize(this);
36966 this.activeMinSize = this.getMinimumSize();;
36967 this.activeMaxSize = this.getMaximumSize();;
36968 var c1 = size - this.activeMinSize;
36969 var c2 = Math.max(this.activeMaxSize - size, 0);
36970 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36971 this.dd.resetConstraints();
36972 this.dd.setXConstraint(
36973 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36974 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36976 this.dd.setYConstraint(0, 0);
36978 this.dd.resetConstraints();
36979 this.dd.setXConstraint(0, 0);
36980 this.dd.setYConstraint(
36981 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36982 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36985 this.dragSpecs.startSize = size;
36986 this.dragSpecs.startPoint = [x, y];
36987 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36991 * @private Called after the drag operation by the DDProxy
36993 onEndProxyDrag : function(e){
36994 Roo.get(this.proxy).setDisplayed(false);
36995 var endPoint = Roo.lib.Event.getXY(e);
36997 this.overlay.hide();
37000 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37001 newSize = this.dragSpecs.startSize +
37002 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37003 endPoint[0] - this.dragSpecs.startPoint[0] :
37004 this.dragSpecs.startPoint[0] - endPoint[0]
37007 newSize = this.dragSpecs.startSize +
37008 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37009 endPoint[1] - this.dragSpecs.startPoint[1] :
37010 this.dragSpecs.startPoint[1] - endPoint[1]
37013 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37014 if(newSize != this.dragSpecs.startSize){
37015 if(this.fireEvent('beforeapply', this, newSize) !== false){
37016 this.adapter.setElementSize(this, newSize);
37017 this.fireEvent("moved", this, newSize);
37018 this.fireEvent("resize", this, newSize);
37024 * Get the adapter this SplitBar uses
37025 * @return The adapter object
37027 getAdapter : function(){
37028 return this.adapter;
37032 * Set the adapter this SplitBar uses
37033 * @param {Object} adapter A SplitBar adapter object
37035 setAdapter : function(adapter){
37036 this.adapter = adapter;
37037 this.adapter.init(this);
37041 * Gets the minimum size for the resizing element
37042 * @return {Number} The minimum size
37044 getMinimumSize : function(){
37045 return this.minSize;
37049 * Sets the minimum size for the resizing element
37050 * @param {Number} minSize The minimum size
37052 setMinimumSize : function(minSize){
37053 this.minSize = minSize;
37057 * Gets the maximum size for the resizing element
37058 * @return {Number} The maximum size
37060 getMaximumSize : function(){
37061 return this.maxSize;
37065 * Sets the maximum size for the resizing element
37066 * @param {Number} maxSize The maximum size
37068 setMaximumSize : function(maxSize){
37069 this.maxSize = maxSize;
37073 * Sets the initialize size for the resizing element
37074 * @param {Number} size The initial size
37076 setCurrentSize : function(size){
37077 var oldAnimate = this.animate;
37078 this.animate = false;
37079 this.adapter.setElementSize(this, size);
37080 this.animate = oldAnimate;
37084 * Destroy this splitbar.
37085 * @param {Boolean} removeEl True to remove the element
37087 destroy : function(removeEl){
37089 this.shim.remove();
37092 this.proxy.parentNode.removeChild(this.proxy);
37100 * @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.
37102 Roo.bootstrap.SplitBar.createProxy = function(dir){
37103 var proxy = new Roo.Element(document.createElement("div"));
37104 proxy.unselectable();
37105 var cls = 'roo-splitbar-proxy';
37106 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37107 document.body.appendChild(proxy.dom);
37112 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37113 * Default Adapter. It assumes the splitter and resizing element are not positioned
37114 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37116 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37119 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37120 // do nothing for now
37121 init : function(s){
37125 * Called before drag operations to get the current size of the resizing element.
37126 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37128 getElementSize : function(s){
37129 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37130 return s.resizingEl.getWidth();
37132 return s.resizingEl.getHeight();
37137 * Called after drag operations to set the size of the resizing element.
37138 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37139 * @param {Number} newSize The new size to set
37140 * @param {Function} onComplete A function to be invoked when resizing is complete
37142 setElementSize : function(s, newSize, onComplete){
37143 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37145 s.resizingEl.setWidth(newSize);
37147 onComplete(s, newSize);
37150 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37155 s.resizingEl.setHeight(newSize);
37157 onComplete(s, newSize);
37160 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37167 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37168 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37169 * Adapter that moves the splitter element to align with the resized sizing element.
37170 * Used with an absolute positioned SplitBar.
37171 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37172 * document.body, make sure you assign an id to the body element.
37174 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37175 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37176 this.container = Roo.get(container);
37179 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37180 init : function(s){
37181 this.basic.init(s);
37184 getElementSize : function(s){
37185 return this.basic.getElementSize(s);
37188 setElementSize : function(s, newSize, onComplete){
37189 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37192 moveSplitter : function(s){
37193 var yes = Roo.bootstrap.SplitBar;
37194 switch(s.placement){
37196 s.el.setX(s.resizingEl.getRight());
37199 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37202 s.el.setY(s.resizingEl.getBottom());
37205 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37212 * Orientation constant - Create a vertical SplitBar
37216 Roo.bootstrap.SplitBar.VERTICAL = 1;
37219 * Orientation constant - Create a horizontal SplitBar
37223 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37226 * Placement constant - The resizing element is to the left of the splitter element
37230 Roo.bootstrap.SplitBar.LEFT = 1;
37233 * Placement constant - The resizing element is to the right of the splitter element
37237 Roo.bootstrap.SplitBar.RIGHT = 2;
37240 * Placement constant - The resizing element is positioned above the splitter element
37244 Roo.bootstrap.SplitBar.TOP = 3;
37247 * Placement constant - The resizing element is positioned under splitter element
37251 Roo.bootstrap.SplitBar.BOTTOM = 4;
37252 Roo.namespace("Roo.bootstrap.layout");/*
37254 * Ext JS Library 1.1.1
37255 * Copyright(c) 2006-2007, Ext JS, LLC.
37257 * Originally Released Under LGPL - original licence link has changed is not relivant.
37260 * <script type="text/javascript">
37264 * @class Roo.bootstrap.layout.Manager
37265 * @extends Roo.bootstrap.Component
37266 * Base class for layout managers.
37268 Roo.bootstrap.layout.Manager = function(config)
37270 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37276 /** false to disable window resize monitoring @type Boolean */
37277 this.monitorWindowResize = true;
37282 * Fires when a layout is performed.
37283 * @param {Roo.LayoutManager} this
37287 * @event regionresized
37288 * Fires when the user resizes a region.
37289 * @param {Roo.LayoutRegion} region The resized region
37290 * @param {Number} newSize The new size (width for east/west, height for north/south)
37292 "regionresized" : true,
37294 * @event regioncollapsed
37295 * Fires when a region is collapsed.
37296 * @param {Roo.LayoutRegion} region The collapsed region
37298 "regioncollapsed" : true,
37300 * @event regionexpanded
37301 * Fires when a region is expanded.
37302 * @param {Roo.LayoutRegion} region The expanded region
37304 "regionexpanded" : true
37306 this.updating = false;
37309 this.el = Roo.get(config.el);
37315 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37320 monitorWindowResize : true,
37326 onRender : function(ct, position)
37329 this.el = Roo.get(ct);
37332 //this.fireEvent('render',this);
37336 initEvents: function()
37340 // ie scrollbar fix
37341 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37342 document.body.scroll = "no";
37343 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37344 this.el.position('relative');
37346 this.id = this.el.id;
37347 this.el.addClass("roo-layout-container");
37348 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37349 if(this.el.dom != document.body ) {
37350 this.el.on('resize', this.layout,this);
37351 this.el.on('show', this.layout,this);
37357 * Returns true if this layout is currently being updated
37358 * @return {Boolean}
37360 isUpdating : function(){
37361 return this.updating;
37365 * Suspend the LayoutManager from doing auto-layouts while
37366 * making multiple add or remove calls
37368 beginUpdate : function(){
37369 this.updating = true;
37373 * Restore auto-layouts and optionally disable the manager from performing a layout
37374 * @param {Boolean} noLayout true to disable a layout update
37376 endUpdate : function(noLayout){
37377 this.updating = false;
37383 layout: function(){
37387 onRegionResized : function(region, newSize){
37388 this.fireEvent("regionresized", region, newSize);
37392 onRegionCollapsed : function(region){
37393 this.fireEvent("regioncollapsed", region);
37396 onRegionExpanded : function(region){
37397 this.fireEvent("regionexpanded", region);
37401 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37402 * performs box-model adjustments.
37403 * @return {Object} The size as an object {width: (the width), height: (the height)}
37405 getViewSize : function()
37408 if(this.el.dom != document.body){
37409 size = this.el.getSize();
37411 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37413 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37414 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37419 * Returns the Element this layout is bound to.
37420 * @return {Roo.Element}
37422 getEl : function(){
37427 * Returns the specified region.
37428 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37429 * @return {Roo.LayoutRegion}
37431 getRegion : function(target){
37432 return this.regions[target.toLowerCase()];
37435 onWindowResize : function(){
37436 if(this.monitorWindowResize){
37443 * Ext JS Library 1.1.1
37444 * Copyright(c) 2006-2007, Ext JS, LLC.
37446 * Originally Released Under LGPL - original licence link has changed is not relivant.
37449 * <script type="text/javascript">
37452 * @class Roo.bootstrap.layout.Border
37453 * @extends Roo.bootstrap.layout.Manager
37454 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37455 * please see: examples/bootstrap/nested.html<br><br>
37457 <b>The container the layout is rendered into can be either the body element or any other element.
37458 If it is not the body element, the container needs to either be an absolute positioned element,
37459 or you will need to add "position:relative" to the css of the container. You will also need to specify
37460 the container size if it is not the body element.</b>
37463 * Create a new Border
37464 * @param {Object} config Configuration options
37466 Roo.bootstrap.layout.Border = function(config){
37467 config = config || {};
37468 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37472 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37473 if(config[region]){
37474 config[region].region = region;
37475 this.addRegion(config[region]);
37481 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37483 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37485 parent : false, // this might point to a 'nest' or a ???
37488 * Creates and adds a new region if it doesn't already exist.
37489 * @param {String} target The target region key (north, south, east, west or center).
37490 * @param {Object} config The regions config object
37491 * @return {BorderLayoutRegion} The new region
37493 addRegion : function(config)
37495 if(!this.regions[config.region]){
37496 var r = this.factory(config);
37497 this.bindRegion(r);
37499 return this.regions[config.region];
37503 bindRegion : function(r){
37504 this.regions[r.config.region] = r;
37506 r.on("visibilitychange", this.layout, this);
37507 r.on("paneladded", this.layout, this);
37508 r.on("panelremoved", this.layout, this);
37509 r.on("invalidated", this.layout, this);
37510 r.on("resized", this.onRegionResized, this);
37511 r.on("collapsed", this.onRegionCollapsed, this);
37512 r.on("expanded", this.onRegionExpanded, this);
37516 * Performs a layout update.
37518 layout : function()
37520 if(this.updating) {
37524 // render all the rebions if they have not been done alreayd?
37525 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37526 if(this.regions[region] && !this.regions[region].bodyEl){
37527 this.regions[region].onRender(this.el)
37531 var size = this.getViewSize();
37532 var w = size.width;
37533 var h = size.height;
37538 //var x = 0, y = 0;
37540 var rs = this.regions;
37541 var north = rs["north"];
37542 var south = rs["south"];
37543 var west = rs["west"];
37544 var east = rs["east"];
37545 var center = rs["center"];
37546 //if(this.hideOnLayout){ // not supported anymore
37547 //c.el.setStyle("display", "none");
37549 if(north && north.isVisible()){
37550 var b = north.getBox();
37551 var m = north.getMargins();
37552 b.width = w - (m.left+m.right);
37555 centerY = b.height + b.y + m.bottom;
37556 centerH -= centerY;
37557 north.updateBox(this.safeBox(b));
37559 if(south && south.isVisible()){
37560 var b = south.getBox();
37561 var m = south.getMargins();
37562 b.width = w - (m.left+m.right);
37564 var totalHeight = (b.height + m.top + m.bottom);
37565 b.y = h - totalHeight + m.top;
37566 centerH -= totalHeight;
37567 south.updateBox(this.safeBox(b));
37569 if(west && west.isVisible()){
37570 var b = west.getBox();
37571 var m = west.getMargins();
37572 b.height = centerH - (m.top+m.bottom);
37574 b.y = centerY + m.top;
37575 var totalWidth = (b.width + m.left + m.right);
37576 centerX += totalWidth;
37577 centerW -= totalWidth;
37578 west.updateBox(this.safeBox(b));
37580 if(east && east.isVisible()){
37581 var b = east.getBox();
37582 var m = east.getMargins();
37583 b.height = centerH - (m.top+m.bottom);
37584 var totalWidth = (b.width + m.left + m.right);
37585 b.x = w - totalWidth + m.left;
37586 b.y = centerY + m.top;
37587 centerW -= totalWidth;
37588 east.updateBox(this.safeBox(b));
37591 var m = center.getMargins();
37593 x: centerX + m.left,
37594 y: centerY + m.top,
37595 width: centerW - (m.left+m.right),
37596 height: centerH - (m.top+m.bottom)
37598 //if(this.hideOnLayout){
37599 //center.el.setStyle("display", "block");
37601 center.updateBox(this.safeBox(centerBox));
37604 this.fireEvent("layout", this);
37608 safeBox : function(box){
37609 box.width = Math.max(0, box.width);
37610 box.height = Math.max(0, box.height);
37615 * Adds a ContentPanel (or subclass) to this layout.
37616 * @param {String} target The target region key (north, south, east, west or center).
37617 * @param {Roo.ContentPanel} panel The panel to add
37618 * @return {Roo.ContentPanel} The added panel
37620 add : function(target, panel){
37622 target = target.toLowerCase();
37623 return this.regions[target].add(panel);
37627 * Remove a ContentPanel (or subclass) to this layout.
37628 * @param {String} target The target region key (north, south, east, west or center).
37629 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37630 * @return {Roo.ContentPanel} The removed panel
37632 remove : function(target, panel){
37633 target = target.toLowerCase();
37634 return this.regions[target].remove(panel);
37638 * Searches all regions for a panel with the specified id
37639 * @param {String} panelId
37640 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37642 findPanel : function(panelId){
37643 var rs = this.regions;
37644 for(var target in rs){
37645 if(typeof rs[target] != "function"){
37646 var p = rs[target].getPanel(panelId);
37656 * Searches all regions for a panel with the specified id and activates (shows) it.
37657 * @param {String/ContentPanel} panelId The panels id or the panel itself
37658 * @return {Roo.ContentPanel} The shown panel or null
37660 showPanel : function(panelId) {
37661 var rs = this.regions;
37662 for(var target in rs){
37663 var r = rs[target];
37664 if(typeof r != "function"){
37665 if(r.hasPanel(panelId)){
37666 return r.showPanel(panelId);
37674 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37675 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37678 restoreState : function(provider){
37680 provider = Roo.state.Manager;
37682 var sm = new Roo.LayoutStateManager();
37683 sm.init(this, provider);
37689 * Adds a xtype elements to the layout.
37693 xtype : 'ContentPanel',
37700 xtype : 'NestedLayoutPanel',
37706 items : [ ... list of content panels or nested layout panels.. ]
37710 * @param {Object} cfg Xtype definition of item to add.
37712 addxtype : function(cfg)
37714 // basically accepts a pannel...
37715 // can accept a layout region..!?!?
37716 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37719 // theory? children can only be panels??
37721 //if (!cfg.xtype.match(/Panel$/)) {
37726 if (typeof(cfg.region) == 'undefined') {
37727 Roo.log("Failed to add Panel, region was not set");
37731 var region = cfg.region;
37737 xitems = cfg.items;
37742 if ( region == 'center') {
37743 Roo.log("Center: " + cfg.title);
37749 case 'Content': // ContentPanel (el, cfg)
37750 case 'Scroll': // ContentPanel (el, cfg)
37752 cfg.autoCreate = cfg.autoCreate || true;
37753 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37755 // var el = this.el.createChild();
37756 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37759 this.add(region, ret);
37763 case 'TreePanel': // our new panel!
37764 cfg.el = this.el.createChild();
37765 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37766 this.add(region, ret);
37771 // create a new Layout (which is a Border Layout...
37773 var clayout = cfg.layout;
37774 clayout.el = this.el.createChild();
37775 clayout.items = clayout.items || [];
37779 // replace this exitems with the clayout ones..
37780 xitems = clayout.items;
37782 // force background off if it's in center...
37783 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37784 cfg.background = false;
37786 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37789 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37790 //console.log('adding nested layout panel ' + cfg.toSource());
37791 this.add(region, ret);
37792 nb = {}; /// find first...
37797 // needs grid and region
37799 //var el = this.getRegion(region).el.createChild();
37801 *var el = this.el.createChild();
37802 // create the grid first...
37803 cfg.grid.container = el;
37804 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37807 if (region == 'center' && this.active ) {
37808 cfg.background = false;
37811 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37813 this.add(region, ret);
37815 if (cfg.background) {
37816 // render grid on panel activation (if panel background)
37817 ret.on('activate', function(gp) {
37818 if (!gp.grid.rendered) {
37819 // gp.grid.render(el);
37823 // cfg.grid.render(el);
37829 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37830 // it was the old xcomponent building that caused this before.
37831 // espeically if border is the top element in the tree.
37841 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37843 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37844 this.add(region, ret);
37848 throw "Can not add '" + cfg.xtype + "' to Border";
37854 this.beginUpdate();
37858 Roo.each(xitems, function(i) {
37859 region = nb && i.region ? i.region : false;
37861 var add = ret.addxtype(i);
37864 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37865 if (!i.background) {
37866 abn[region] = nb[region] ;
37873 // make the last non-background panel active..
37874 //if (nb) { Roo.log(abn); }
37877 for(var r in abn) {
37878 region = this.getRegion(r);
37880 // tried using nb[r], but it does not work..
37882 region.showPanel(abn[r]);
37893 factory : function(cfg)
37896 var validRegions = Roo.bootstrap.layout.Border.regions;
37898 var target = cfg.region;
37901 var r = Roo.bootstrap.layout;
37905 return new r.North(cfg);
37907 return new r.South(cfg);
37909 return new r.East(cfg);
37911 return new r.West(cfg);
37913 return new r.Center(cfg);
37915 throw 'Layout region "'+target+'" not supported.';
37922 * Ext JS Library 1.1.1
37923 * Copyright(c) 2006-2007, Ext JS, LLC.
37925 * Originally Released Under LGPL - original licence link has changed is not relivant.
37928 * <script type="text/javascript">
37932 * @class Roo.bootstrap.layout.Basic
37933 * @extends Roo.util.Observable
37934 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37935 * and does not have a titlebar, tabs or any other features. All it does is size and position
37936 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37937 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37938 * @cfg {string} region the region that it inhabits..
37939 * @cfg {bool} skipConfig skip config?
37943 Roo.bootstrap.layout.Basic = function(config){
37945 this.mgr = config.mgr;
37947 this.position = config.region;
37949 var skipConfig = config.skipConfig;
37953 * @scope Roo.BasicLayoutRegion
37957 * @event beforeremove
37958 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37959 * @param {Roo.LayoutRegion} this
37960 * @param {Roo.ContentPanel} panel The panel
37961 * @param {Object} e The cancel event object
37963 "beforeremove" : true,
37965 * @event invalidated
37966 * Fires when the layout for this region is changed.
37967 * @param {Roo.LayoutRegion} this
37969 "invalidated" : true,
37971 * @event visibilitychange
37972 * Fires when this region is shown or hidden
37973 * @param {Roo.LayoutRegion} this
37974 * @param {Boolean} visibility true or false
37976 "visibilitychange" : true,
37978 * @event paneladded
37979 * Fires when a panel is added.
37980 * @param {Roo.LayoutRegion} this
37981 * @param {Roo.ContentPanel} panel The panel
37983 "paneladded" : true,
37985 * @event panelremoved
37986 * Fires when a panel is removed.
37987 * @param {Roo.LayoutRegion} this
37988 * @param {Roo.ContentPanel} panel The panel
37990 "panelremoved" : true,
37992 * @event beforecollapse
37993 * Fires when this region before collapse.
37994 * @param {Roo.LayoutRegion} this
37996 "beforecollapse" : true,
37999 * Fires when this region is collapsed.
38000 * @param {Roo.LayoutRegion} this
38002 "collapsed" : true,
38005 * Fires when this region is expanded.
38006 * @param {Roo.LayoutRegion} this
38011 * Fires when this region is slid into view.
38012 * @param {Roo.LayoutRegion} this
38014 "slideshow" : true,
38017 * Fires when this region slides out of view.
38018 * @param {Roo.LayoutRegion} this
38020 "slidehide" : true,
38022 * @event panelactivated
38023 * Fires when a panel is activated.
38024 * @param {Roo.LayoutRegion} this
38025 * @param {Roo.ContentPanel} panel The activated panel
38027 "panelactivated" : true,
38030 * Fires when the user resizes this region.
38031 * @param {Roo.LayoutRegion} this
38032 * @param {Number} newSize The new size (width for east/west, height for north/south)
38036 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38037 this.panels = new Roo.util.MixedCollection();
38038 this.panels.getKey = this.getPanelId.createDelegate(this);
38040 this.activePanel = null;
38041 // ensure listeners are added...
38043 if (config.listeners || config.events) {
38044 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38045 listeners : config.listeners || {},
38046 events : config.events || {}
38050 if(skipConfig !== true){
38051 this.applyConfig(config);
38055 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38057 getPanelId : function(p){
38061 applyConfig : function(config){
38062 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38063 this.config = config;
38068 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38069 * the width, for horizontal (north, south) the height.
38070 * @param {Number} newSize The new width or height
38072 resizeTo : function(newSize){
38073 var el = this.el ? this.el :
38074 (this.activePanel ? this.activePanel.getEl() : null);
38076 switch(this.position){
38079 el.setWidth(newSize);
38080 this.fireEvent("resized", this, newSize);
38084 el.setHeight(newSize);
38085 this.fireEvent("resized", this, newSize);
38091 getBox : function(){
38092 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38095 getMargins : function(){
38096 return this.margins;
38099 updateBox : function(box){
38101 var el = this.activePanel.getEl();
38102 el.dom.style.left = box.x + "px";
38103 el.dom.style.top = box.y + "px";
38104 this.activePanel.setSize(box.width, box.height);
38108 * Returns the container element for this region.
38109 * @return {Roo.Element}
38111 getEl : function(){
38112 return this.activePanel;
38116 * Returns true if this region is currently visible.
38117 * @return {Boolean}
38119 isVisible : function(){
38120 return this.activePanel ? true : false;
38123 setActivePanel : function(panel){
38124 panel = this.getPanel(panel);
38125 if(this.activePanel && this.activePanel != panel){
38126 this.activePanel.setActiveState(false);
38127 this.activePanel.getEl().setLeftTop(-10000,-10000);
38129 this.activePanel = panel;
38130 panel.setActiveState(true);
38132 panel.setSize(this.box.width, this.box.height);
38134 this.fireEvent("panelactivated", this, panel);
38135 this.fireEvent("invalidated");
38139 * Show the specified panel.
38140 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38141 * @return {Roo.ContentPanel} The shown panel or null
38143 showPanel : function(panel){
38144 panel = this.getPanel(panel);
38146 this.setActivePanel(panel);
38152 * Get the active panel for this region.
38153 * @return {Roo.ContentPanel} The active panel or null
38155 getActivePanel : function(){
38156 return this.activePanel;
38160 * Add the passed ContentPanel(s)
38161 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38162 * @return {Roo.ContentPanel} The panel added (if only one was added)
38164 add : function(panel){
38165 if(arguments.length > 1){
38166 for(var i = 0, len = arguments.length; i < len; i++) {
38167 this.add(arguments[i]);
38171 if(this.hasPanel(panel)){
38172 this.showPanel(panel);
38175 var el = panel.getEl();
38176 if(el.dom.parentNode != this.mgr.el.dom){
38177 this.mgr.el.dom.appendChild(el.dom);
38179 if(panel.setRegion){
38180 panel.setRegion(this);
38182 this.panels.add(panel);
38183 el.setStyle("position", "absolute");
38184 if(!panel.background){
38185 this.setActivePanel(panel);
38186 if(this.config.initialSize && this.panels.getCount()==1){
38187 this.resizeTo(this.config.initialSize);
38190 this.fireEvent("paneladded", this, panel);
38195 * Returns true if the panel is in this region.
38196 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38197 * @return {Boolean}
38199 hasPanel : function(panel){
38200 if(typeof panel == "object"){ // must be panel obj
38201 panel = panel.getId();
38203 return this.getPanel(panel) ? true : false;
38207 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38208 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38209 * @param {Boolean} preservePanel Overrides the config preservePanel option
38210 * @return {Roo.ContentPanel} The panel that was removed
38212 remove : function(panel, preservePanel){
38213 panel = this.getPanel(panel);
38218 this.fireEvent("beforeremove", this, panel, e);
38219 if(e.cancel === true){
38222 var panelId = panel.getId();
38223 this.panels.removeKey(panelId);
38228 * Returns the panel specified or null if it's not in this region.
38229 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38230 * @return {Roo.ContentPanel}
38232 getPanel : function(id){
38233 if(typeof id == "object"){ // must be panel obj
38236 return this.panels.get(id);
38240 * Returns this regions position (north/south/east/west/center).
38243 getPosition: function(){
38244 return this.position;
38248 * Ext JS Library 1.1.1
38249 * Copyright(c) 2006-2007, Ext JS, LLC.
38251 * Originally Released Under LGPL - original licence link has changed is not relivant.
38254 * <script type="text/javascript">
38258 * @class Roo.bootstrap.layout.Region
38259 * @extends Roo.bootstrap.layout.Basic
38260 * This class represents a region in a layout manager.
38262 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38263 * @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})
38264 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38265 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38266 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38267 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38268 * @cfg {String} title The title for the region (overrides panel titles)
38269 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38270 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38271 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38272 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38273 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38274 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38275 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38276 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38277 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38278 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38280 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38281 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38282 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38283 * @cfg {Number} width For East/West panels
38284 * @cfg {Number} height For North/South panels
38285 * @cfg {Boolean} split To show the splitter
38286 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38288 * @cfg {string} cls Extra CSS classes to add to region
38290 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38291 * @cfg {string} region the region that it inhabits..
38294 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38295 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38297 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38298 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38299 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38301 Roo.bootstrap.layout.Region = function(config)
38303 this.applyConfig(config);
38305 var mgr = config.mgr;
38306 var pos = config.region;
38307 config.skipConfig = true;
38308 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38311 this.onRender(mgr.el);
38314 this.visible = true;
38315 this.collapsed = false;
38316 this.unrendered_panels = [];
38319 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38321 position: '', // set by wrapper (eg. north/south etc..)
38322 unrendered_panels : null, // unrendered panels.
38324 tabPosition : false,
38326 mgr: false, // points to 'Border'
38329 createBody : function(){
38330 /** This region's body element
38331 * @type Roo.Element */
38332 this.bodyEl = this.el.createChild({
38334 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38338 onRender: function(ctr, pos)
38340 var dh = Roo.DomHelper;
38341 /** This region's container element
38342 * @type Roo.Element */
38343 this.el = dh.append(ctr.dom, {
38345 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38347 /** This region's title element
38348 * @type Roo.Element */
38350 this.titleEl = dh.append(this.el.dom, {
38352 unselectable: "on",
38353 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38355 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38356 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38360 this.titleEl.enableDisplayMode();
38361 /** This region's title text element
38362 * @type HTMLElement */
38363 this.titleTextEl = this.titleEl.dom.firstChild;
38364 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38366 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38367 this.closeBtn.enableDisplayMode();
38368 this.closeBtn.on("click", this.closeClicked, this);
38369 this.closeBtn.hide();
38371 this.createBody(this.config);
38372 if(this.config.hideWhenEmpty){
38374 this.on("paneladded", this.validateVisibility, this);
38375 this.on("panelremoved", this.validateVisibility, this);
38377 if(this.autoScroll){
38378 this.bodyEl.setStyle("overflow", "auto");
38380 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38382 //if(c.titlebar !== false){
38383 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38384 this.titleEl.hide();
38386 this.titleEl.show();
38387 if(this.config.title){
38388 this.titleTextEl.innerHTML = this.config.title;
38392 if(this.config.collapsed){
38393 this.collapse(true);
38395 if(this.config.hidden){
38399 if (this.unrendered_panels && this.unrendered_panels.length) {
38400 for (var i =0;i< this.unrendered_panels.length; i++) {
38401 this.add(this.unrendered_panels[i]);
38403 this.unrendered_panels = null;
38409 applyConfig : function(c)
38412 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38413 var dh = Roo.DomHelper;
38414 if(c.titlebar !== false){
38415 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38416 this.collapseBtn.on("click", this.collapse, this);
38417 this.collapseBtn.enableDisplayMode();
38419 if(c.showPin === true || this.showPin){
38420 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38421 this.stickBtn.enableDisplayMode();
38422 this.stickBtn.on("click", this.expand, this);
38423 this.stickBtn.hide();
38428 /** This region's collapsed element
38429 * @type Roo.Element */
38432 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38433 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38436 if(c.floatable !== false){
38437 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38438 this.collapsedEl.on("click", this.collapseClick, this);
38441 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38442 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38443 id: "message", unselectable: "on", style:{"float":"left"}});
38444 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38446 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38447 this.expandBtn.on("click", this.expand, this);
38451 if(this.collapseBtn){
38452 this.collapseBtn.setVisible(c.collapsible == true);
38455 this.cmargins = c.cmargins || this.cmargins ||
38456 (this.position == "west" || this.position == "east" ?
38457 {top: 0, left: 2, right:2, bottom: 0} :
38458 {top: 2, left: 0, right:0, bottom: 2});
38460 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38463 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38465 this.autoScroll = c.autoScroll || false;
38470 this.duration = c.duration || .30;
38471 this.slideDuration = c.slideDuration || .45;
38476 * Returns true if this region is currently visible.
38477 * @return {Boolean}
38479 isVisible : function(){
38480 return this.visible;
38484 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38485 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38487 //setCollapsedTitle : function(title){
38488 // title = title || " ";
38489 // if(this.collapsedTitleTextEl){
38490 // this.collapsedTitleTextEl.innerHTML = title;
38494 getBox : function(){
38496 // if(!this.collapsed){
38497 b = this.el.getBox(false, true);
38499 // b = this.collapsedEl.getBox(false, true);
38504 getMargins : function(){
38505 return this.margins;
38506 //return this.collapsed ? this.cmargins : this.margins;
38509 highlight : function(){
38510 this.el.addClass("x-layout-panel-dragover");
38513 unhighlight : function(){
38514 this.el.removeClass("x-layout-panel-dragover");
38517 updateBox : function(box)
38519 if (!this.bodyEl) {
38520 return; // not rendered yet..
38524 if(!this.collapsed){
38525 this.el.dom.style.left = box.x + "px";
38526 this.el.dom.style.top = box.y + "px";
38527 this.updateBody(box.width, box.height);
38529 this.collapsedEl.dom.style.left = box.x + "px";
38530 this.collapsedEl.dom.style.top = box.y + "px";
38531 this.collapsedEl.setSize(box.width, box.height);
38534 this.tabs.autoSizeTabs();
38538 updateBody : function(w, h)
38541 this.el.setWidth(w);
38542 w -= this.el.getBorderWidth("rl");
38543 if(this.config.adjustments){
38544 w += this.config.adjustments[0];
38547 if(h !== null && h > 0){
38548 this.el.setHeight(h);
38549 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38550 h -= this.el.getBorderWidth("tb");
38551 if(this.config.adjustments){
38552 h += this.config.adjustments[1];
38554 this.bodyEl.setHeight(h);
38556 h = this.tabs.syncHeight(h);
38559 if(this.panelSize){
38560 w = w !== null ? w : this.panelSize.width;
38561 h = h !== null ? h : this.panelSize.height;
38563 if(this.activePanel){
38564 var el = this.activePanel.getEl();
38565 w = w !== null ? w : el.getWidth();
38566 h = h !== null ? h : el.getHeight();
38567 this.panelSize = {width: w, height: h};
38568 this.activePanel.setSize(w, h);
38570 if(Roo.isIE && this.tabs){
38571 this.tabs.el.repaint();
38576 * Returns the container element for this region.
38577 * @return {Roo.Element}
38579 getEl : function(){
38584 * Hides this region.
38587 //if(!this.collapsed){
38588 this.el.dom.style.left = "-2000px";
38591 // this.collapsedEl.dom.style.left = "-2000px";
38592 // this.collapsedEl.hide();
38594 this.visible = false;
38595 this.fireEvent("visibilitychange", this, false);
38599 * Shows this region if it was previously hidden.
38602 //if(!this.collapsed){
38605 // this.collapsedEl.show();
38607 this.visible = true;
38608 this.fireEvent("visibilitychange", this, true);
38611 closeClicked : function(){
38612 if(this.activePanel){
38613 this.remove(this.activePanel);
38617 collapseClick : function(e){
38619 e.stopPropagation();
38622 e.stopPropagation();
38628 * Collapses this region.
38629 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38632 collapse : function(skipAnim, skipCheck = false){
38633 if(this.collapsed) {
38637 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38639 this.collapsed = true;
38641 this.split.el.hide();
38643 if(this.config.animate && skipAnim !== true){
38644 this.fireEvent("invalidated", this);
38645 this.animateCollapse();
38647 this.el.setLocation(-20000,-20000);
38649 this.collapsedEl.show();
38650 this.fireEvent("collapsed", this);
38651 this.fireEvent("invalidated", this);
38657 animateCollapse : function(){
38662 * Expands this region if it was previously collapsed.
38663 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38664 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38667 expand : function(e, skipAnim){
38669 e.stopPropagation();
38671 if(!this.collapsed || this.el.hasActiveFx()) {
38675 this.afterSlideIn();
38678 this.collapsed = false;
38679 if(this.config.animate && skipAnim !== true){
38680 this.animateExpand();
38684 this.split.el.show();
38686 this.collapsedEl.setLocation(-2000,-2000);
38687 this.collapsedEl.hide();
38688 this.fireEvent("invalidated", this);
38689 this.fireEvent("expanded", this);
38693 animateExpand : function(){
38697 initTabs : function()
38699 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38701 var ts = new Roo.bootstrap.panel.Tabs({
38702 el: this.bodyEl.dom,
38704 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38705 disableTooltips: this.config.disableTabTips,
38706 toolbar : this.config.toolbar
38709 if(this.config.hideTabs){
38710 ts.stripWrap.setDisplayed(false);
38713 ts.resizeTabs = this.config.resizeTabs === true;
38714 ts.minTabWidth = this.config.minTabWidth || 40;
38715 ts.maxTabWidth = this.config.maxTabWidth || 250;
38716 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38717 ts.monitorResize = false;
38718 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38719 ts.bodyEl.addClass('roo-layout-tabs-body');
38720 this.panels.each(this.initPanelAsTab, this);
38723 initPanelAsTab : function(panel){
38724 var ti = this.tabs.addTab(
38728 this.config.closeOnTab && panel.isClosable(),
38731 if(panel.tabTip !== undefined){
38732 ti.setTooltip(panel.tabTip);
38734 ti.on("activate", function(){
38735 this.setActivePanel(panel);
38738 if(this.config.closeOnTab){
38739 ti.on("beforeclose", function(t, e){
38741 this.remove(panel);
38745 panel.tabItem = ti;
38750 updatePanelTitle : function(panel, title)
38752 if(this.activePanel == panel){
38753 this.updateTitle(title);
38756 var ti = this.tabs.getTab(panel.getEl().id);
38758 if(panel.tabTip !== undefined){
38759 ti.setTooltip(panel.tabTip);
38764 updateTitle : function(title){
38765 if(this.titleTextEl && !this.config.title){
38766 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38770 setActivePanel : function(panel)
38772 panel = this.getPanel(panel);
38773 if(this.activePanel && this.activePanel != panel){
38774 if(this.activePanel.setActiveState(false) === false){
38778 this.activePanel = panel;
38779 panel.setActiveState(true);
38780 if(this.panelSize){
38781 panel.setSize(this.panelSize.width, this.panelSize.height);
38784 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38786 this.updateTitle(panel.getTitle());
38788 this.fireEvent("invalidated", this);
38790 this.fireEvent("panelactivated", this, panel);
38794 * Shows the specified panel.
38795 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38796 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38798 showPanel : function(panel)
38800 panel = this.getPanel(panel);
38803 var tab = this.tabs.getTab(panel.getEl().id);
38804 if(tab.isHidden()){
38805 this.tabs.unhideTab(tab.id);
38809 this.setActivePanel(panel);
38816 * Get the active panel for this region.
38817 * @return {Roo.ContentPanel} The active panel or null
38819 getActivePanel : function(){
38820 return this.activePanel;
38823 validateVisibility : function(){
38824 if(this.panels.getCount() < 1){
38825 this.updateTitle(" ");
38826 this.closeBtn.hide();
38829 if(!this.isVisible()){
38836 * Adds the passed ContentPanel(s) to this region.
38837 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38838 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38840 add : function(panel)
38842 if(arguments.length > 1){
38843 for(var i = 0, len = arguments.length; i < len; i++) {
38844 this.add(arguments[i]);
38849 // if we have not been rendered yet, then we can not really do much of this..
38850 if (!this.bodyEl) {
38851 this.unrendered_panels.push(panel);
38858 if(this.hasPanel(panel)){
38859 this.showPanel(panel);
38862 panel.setRegion(this);
38863 this.panels.add(panel);
38864 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38865 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38866 // and hide them... ???
38867 this.bodyEl.dom.appendChild(panel.getEl().dom);
38868 if(panel.background !== true){
38869 this.setActivePanel(panel);
38871 this.fireEvent("paneladded", this, panel);
38878 this.initPanelAsTab(panel);
38882 if(panel.background !== true){
38883 this.tabs.activate(panel.getEl().id);
38885 this.fireEvent("paneladded", this, panel);
38890 * Hides the tab for the specified panel.
38891 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38893 hidePanel : function(panel){
38894 if(this.tabs && (panel = this.getPanel(panel))){
38895 this.tabs.hideTab(panel.getEl().id);
38900 * Unhides the tab for a previously hidden panel.
38901 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38903 unhidePanel : function(panel){
38904 if(this.tabs && (panel = this.getPanel(panel))){
38905 this.tabs.unhideTab(panel.getEl().id);
38909 clearPanels : function(){
38910 while(this.panels.getCount() > 0){
38911 this.remove(this.panels.first());
38916 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38917 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38918 * @param {Boolean} preservePanel Overrides the config preservePanel option
38919 * @return {Roo.ContentPanel} The panel that was removed
38921 remove : function(panel, preservePanel)
38923 panel = this.getPanel(panel);
38928 this.fireEvent("beforeremove", this, panel, e);
38929 if(e.cancel === true){
38932 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38933 var panelId = panel.getId();
38934 this.panels.removeKey(panelId);
38936 document.body.appendChild(panel.getEl().dom);
38939 this.tabs.removeTab(panel.getEl().id);
38940 }else if (!preservePanel){
38941 this.bodyEl.dom.removeChild(panel.getEl().dom);
38943 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38944 var p = this.panels.first();
38945 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38946 tempEl.appendChild(p.getEl().dom);
38947 this.bodyEl.update("");
38948 this.bodyEl.dom.appendChild(p.getEl().dom);
38950 this.updateTitle(p.getTitle());
38952 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38953 this.setActivePanel(p);
38955 panel.setRegion(null);
38956 if(this.activePanel == panel){
38957 this.activePanel = null;
38959 if(this.config.autoDestroy !== false && preservePanel !== true){
38960 try{panel.destroy();}catch(e){}
38962 this.fireEvent("panelremoved", this, panel);
38967 * Returns the TabPanel component used by this region
38968 * @return {Roo.TabPanel}
38970 getTabs : function(){
38974 createTool : function(parentEl, className){
38975 var btn = Roo.DomHelper.append(parentEl, {
38977 cls: "x-layout-tools-button",
38980 cls: "roo-layout-tools-button-inner " + className,
38984 btn.addClassOnOver("roo-layout-tools-button-over");
38989 * Ext JS Library 1.1.1
38990 * Copyright(c) 2006-2007, Ext JS, LLC.
38992 * Originally Released Under LGPL - original licence link has changed is not relivant.
38995 * <script type="text/javascript">
39001 * @class Roo.SplitLayoutRegion
39002 * @extends Roo.LayoutRegion
39003 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39005 Roo.bootstrap.layout.Split = function(config){
39006 this.cursor = config.cursor;
39007 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39010 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39012 splitTip : "Drag to resize.",
39013 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39014 useSplitTips : false,
39016 applyConfig : function(config){
39017 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39020 onRender : function(ctr,pos) {
39022 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39023 if(!this.config.split){
39028 var splitEl = Roo.DomHelper.append(ctr.dom, {
39030 id: this.el.id + "-split",
39031 cls: "roo-layout-split roo-layout-split-"+this.position,
39034 /** The SplitBar for this region
39035 * @type Roo.SplitBar */
39036 // does not exist yet...
39037 Roo.log([this.position, this.orientation]);
39039 this.split = new Roo.bootstrap.SplitBar({
39040 dragElement : splitEl,
39041 resizingElement: this.el,
39042 orientation : this.orientation
39045 this.split.on("moved", this.onSplitMove, this);
39046 this.split.useShim = this.config.useShim === true;
39047 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39048 if(this.useSplitTips){
39049 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39051 //if(config.collapsible){
39052 // this.split.el.on("dblclick", this.collapse, this);
39055 if(typeof this.config.minSize != "undefined"){
39056 this.split.minSize = this.config.minSize;
39058 if(typeof this.config.maxSize != "undefined"){
39059 this.split.maxSize = this.config.maxSize;
39061 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39062 this.hideSplitter();
39067 getHMaxSize : function(){
39068 var cmax = this.config.maxSize || 10000;
39069 var center = this.mgr.getRegion("center");
39070 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39073 getVMaxSize : function(){
39074 var cmax = this.config.maxSize || 10000;
39075 var center = this.mgr.getRegion("center");
39076 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39079 onSplitMove : function(split, newSize){
39080 this.fireEvent("resized", this, newSize);
39084 * Returns the {@link Roo.SplitBar} for this region.
39085 * @return {Roo.SplitBar}
39087 getSplitBar : function(){
39092 this.hideSplitter();
39093 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39096 hideSplitter : function(){
39098 this.split.el.setLocation(-2000,-2000);
39099 this.split.el.hide();
39105 this.split.el.show();
39107 Roo.bootstrap.layout.Split.superclass.show.call(this);
39110 beforeSlide: function(){
39111 if(Roo.isGecko){// firefox overflow auto bug workaround
39112 this.bodyEl.clip();
39114 this.tabs.bodyEl.clip();
39116 if(this.activePanel){
39117 this.activePanel.getEl().clip();
39119 if(this.activePanel.beforeSlide){
39120 this.activePanel.beforeSlide();
39126 afterSlide : function(){
39127 if(Roo.isGecko){// firefox overflow auto bug workaround
39128 this.bodyEl.unclip();
39130 this.tabs.bodyEl.unclip();
39132 if(this.activePanel){
39133 this.activePanel.getEl().unclip();
39134 if(this.activePanel.afterSlide){
39135 this.activePanel.afterSlide();
39141 initAutoHide : function(){
39142 if(this.autoHide !== false){
39143 if(!this.autoHideHd){
39144 var st = new Roo.util.DelayedTask(this.slideIn, this);
39145 this.autoHideHd = {
39146 "mouseout": function(e){
39147 if(!e.within(this.el, true)){
39151 "mouseover" : function(e){
39157 this.el.on(this.autoHideHd);
39161 clearAutoHide : function(){
39162 if(this.autoHide !== false){
39163 this.el.un("mouseout", this.autoHideHd.mouseout);
39164 this.el.un("mouseover", this.autoHideHd.mouseover);
39168 clearMonitor : function(){
39169 Roo.get(document).un("click", this.slideInIf, this);
39172 // these names are backwards but not changed for compat
39173 slideOut : function(){
39174 if(this.isSlid || this.el.hasActiveFx()){
39177 this.isSlid = true;
39178 if(this.collapseBtn){
39179 this.collapseBtn.hide();
39181 this.closeBtnState = this.closeBtn.getStyle('display');
39182 this.closeBtn.hide();
39184 this.stickBtn.show();
39187 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39188 this.beforeSlide();
39189 this.el.setStyle("z-index", 10001);
39190 this.el.slideIn(this.getSlideAnchor(), {
39191 callback: function(){
39193 this.initAutoHide();
39194 Roo.get(document).on("click", this.slideInIf, this);
39195 this.fireEvent("slideshow", this);
39202 afterSlideIn : function(){
39203 this.clearAutoHide();
39204 this.isSlid = false;
39205 this.clearMonitor();
39206 this.el.setStyle("z-index", "");
39207 if(this.collapseBtn){
39208 this.collapseBtn.show();
39210 this.closeBtn.setStyle('display', this.closeBtnState);
39212 this.stickBtn.hide();
39214 this.fireEvent("slidehide", this);
39217 slideIn : function(cb){
39218 if(!this.isSlid || this.el.hasActiveFx()){
39222 this.isSlid = false;
39223 this.beforeSlide();
39224 this.el.slideOut(this.getSlideAnchor(), {
39225 callback: function(){
39226 this.el.setLeftTop(-10000, -10000);
39228 this.afterSlideIn();
39236 slideInIf : function(e){
39237 if(!e.within(this.el)){
39242 animateCollapse : function(){
39243 this.beforeSlide();
39244 this.el.setStyle("z-index", 20000);
39245 var anchor = this.getSlideAnchor();
39246 this.el.slideOut(anchor, {
39247 callback : function(){
39248 this.el.setStyle("z-index", "");
39249 this.collapsedEl.slideIn(anchor, {duration:.3});
39251 this.el.setLocation(-10000,-10000);
39253 this.fireEvent("collapsed", this);
39260 animateExpand : function(){
39261 this.beforeSlide();
39262 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39263 this.el.setStyle("z-index", 20000);
39264 this.collapsedEl.hide({
39267 this.el.slideIn(this.getSlideAnchor(), {
39268 callback : function(){
39269 this.el.setStyle("z-index", "");
39272 this.split.el.show();
39274 this.fireEvent("invalidated", this);
39275 this.fireEvent("expanded", this);
39303 getAnchor : function(){
39304 return this.anchors[this.position];
39307 getCollapseAnchor : function(){
39308 return this.canchors[this.position];
39311 getSlideAnchor : function(){
39312 return this.sanchors[this.position];
39315 getAlignAdj : function(){
39316 var cm = this.cmargins;
39317 switch(this.position){
39333 getExpandAdj : function(){
39334 var c = this.collapsedEl, cm = this.cmargins;
39335 switch(this.position){
39337 return [-(cm.right+c.getWidth()+cm.left), 0];
39340 return [cm.right+c.getWidth()+cm.left, 0];
39343 return [0, -(cm.top+cm.bottom+c.getHeight())];
39346 return [0, cm.top+cm.bottom+c.getHeight()];
39352 * Ext JS Library 1.1.1
39353 * Copyright(c) 2006-2007, Ext JS, LLC.
39355 * Originally Released Under LGPL - original licence link has changed is not relivant.
39358 * <script type="text/javascript">
39361 * These classes are private internal classes
39363 Roo.bootstrap.layout.Center = function(config){
39364 config.region = "center";
39365 Roo.bootstrap.layout.Region.call(this, config);
39366 this.visible = true;
39367 this.minWidth = config.minWidth || 20;
39368 this.minHeight = config.minHeight || 20;
39371 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39373 // center panel can't be hidden
39377 // center panel can't be hidden
39380 getMinWidth: function(){
39381 return this.minWidth;
39384 getMinHeight: function(){
39385 return this.minHeight;
39399 Roo.bootstrap.layout.North = function(config)
39401 config.region = 'north';
39402 config.cursor = 'n-resize';
39404 Roo.bootstrap.layout.Split.call(this, config);
39408 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39409 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39410 this.split.el.addClass("roo-layout-split-v");
39412 //var size = config.initialSize || config.height;
39413 //if(this.el && typeof size != "undefined"){
39414 // this.el.setHeight(size);
39417 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39419 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39422 onRender : function(ctr, pos)
39424 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39425 var size = this.config.initialSize || this.config.height;
39426 if(this.el && typeof size != "undefined"){
39427 this.el.setHeight(size);
39432 getBox : function(){
39433 if(this.collapsed){
39434 return this.collapsedEl.getBox();
39436 var box = this.el.getBox();
39438 box.height += this.split.el.getHeight();
39443 updateBox : function(box){
39444 if(this.split && !this.collapsed){
39445 box.height -= this.split.el.getHeight();
39446 this.split.el.setLeft(box.x);
39447 this.split.el.setTop(box.y+box.height);
39448 this.split.el.setWidth(box.width);
39450 if(this.collapsed){
39451 this.updateBody(box.width, null);
39453 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39461 Roo.bootstrap.layout.South = function(config){
39462 config.region = 'south';
39463 config.cursor = 's-resize';
39464 Roo.bootstrap.layout.Split.call(this, config);
39466 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39467 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39468 this.split.el.addClass("roo-layout-split-v");
39473 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39474 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39476 onRender : function(ctr, pos)
39478 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39479 var size = this.config.initialSize || this.config.height;
39480 if(this.el && typeof size != "undefined"){
39481 this.el.setHeight(size);
39486 getBox : function(){
39487 if(this.collapsed){
39488 return this.collapsedEl.getBox();
39490 var box = this.el.getBox();
39492 var sh = this.split.el.getHeight();
39499 updateBox : function(box){
39500 if(this.split && !this.collapsed){
39501 var sh = this.split.el.getHeight();
39504 this.split.el.setLeft(box.x);
39505 this.split.el.setTop(box.y-sh);
39506 this.split.el.setWidth(box.width);
39508 if(this.collapsed){
39509 this.updateBody(box.width, null);
39511 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39515 Roo.bootstrap.layout.East = function(config){
39516 config.region = "east";
39517 config.cursor = "e-resize";
39518 Roo.bootstrap.layout.Split.call(this, config);
39520 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39521 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39522 this.split.el.addClass("roo-layout-split-h");
39526 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39527 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39529 onRender : function(ctr, pos)
39531 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39532 var size = this.config.initialSize || this.config.width;
39533 if(this.el && typeof size != "undefined"){
39534 this.el.setWidth(size);
39539 getBox : function(){
39540 if(this.collapsed){
39541 return this.collapsedEl.getBox();
39543 var box = this.el.getBox();
39545 var sw = this.split.el.getWidth();
39552 updateBox : function(box){
39553 if(this.split && !this.collapsed){
39554 var sw = this.split.el.getWidth();
39556 this.split.el.setLeft(box.x);
39557 this.split.el.setTop(box.y);
39558 this.split.el.setHeight(box.height);
39561 if(this.collapsed){
39562 this.updateBody(null, box.height);
39564 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39568 Roo.bootstrap.layout.West = function(config){
39569 config.region = "west";
39570 config.cursor = "w-resize";
39572 Roo.bootstrap.layout.Split.call(this, config);
39574 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39575 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39576 this.split.el.addClass("roo-layout-split-h");
39580 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39581 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39583 onRender: function(ctr, pos)
39585 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39586 var size = this.config.initialSize || this.config.width;
39587 if(typeof size != "undefined"){
39588 this.el.setWidth(size);
39592 getBox : function(){
39593 if(this.collapsed){
39594 return this.collapsedEl.getBox();
39596 var box = this.el.getBox();
39597 if (box.width == 0) {
39598 box.width = this.config.width; // kludge?
39601 box.width += this.split.el.getWidth();
39606 updateBox : function(box){
39607 if(this.split && !this.collapsed){
39608 var sw = this.split.el.getWidth();
39610 this.split.el.setLeft(box.x+box.width);
39611 this.split.el.setTop(box.y);
39612 this.split.el.setHeight(box.height);
39614 if(this.collapsed){
39615 this.updateBody(null, box.height);
39617 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39619 });Roo.namespace("Roo.bootstrap.panel");/*
39621 * Ext JS Library 1.1.1
39622 * Copyright(c) 2006-2007, Ext JS, LLC.
39624 * Originally Released Under LGPL - original licence link has changed is not relivant.
39627 * <script type="text/javascript">
39630 * @class Roo.ContentPanel
39631 * @extends Roo.util.Observable
39632 * A basic ContentPanel element.
39633 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39634 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39635 * @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
39636 * @cfg {Boolean} closable True if the panel can be closed/removed
39637 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39638 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39639 * @cfg {Toolbar} toolbar A toolbar for this panel
39640 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39641 * @cfg {String} title The title for this panel
39642 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39643 * @cfg {String} url Calls {@link #setUrl} with this value
39644 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39645 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39646 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39647 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39648 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39649 * @cfg {Boolean} badges render the badges
39650 * @cfg {String} cls extra classes to use
39651 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39654 * Create a new ContentPanel.
39655 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39656 * @param {String/Object} config A string to set only the title or a config object
39657 * @param {String} content (optional) Set the HTML content for this panel
39658 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39660 Roo.bootstrap.panel.Content = function( config){
39662 this.tpl = config.tpl || false;
39664 var el = config.el;
39665 var content = config.content;
39667 if(config.autoCreate){ // xtype is available if this is called from factory
39670 this.el = Roo.get(el);
39671 if(!this.el && config && config.autoCreate){
39672 if(typeof config.autoCreate == "object"){
39673 if(!config.autoCreate.id){
39674 config.autoCreate.id = config.id||el;
39676 this.el = Roo.DomHelper.append(document.body,
39677 config.autoCreate, true);
39681 cls: (config.cls || '') +
39682 (config.background ? ' bg-' + config.background : '') +
39683 " roo-layout-inactive-content",
39686 if (config.iframe) {
39690 style : 'border: 0px',
39691 src : 'about:blank'
39697 elcfg.html = config.html;
39701 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39702 if (config.iframe) {
39703 this.iframeEl = this.el.select('iframe',true).first();
39708 this.closable = false;
39709 this.loaded = false;
39710 this.active = false;
39713 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39715 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39717 this.wrapEl = this.el; //this.el.wrap();
39719 if (config.toolbar.items) {
39720 ti = config.toolbar.items ;
39721 delete config.toolbar.items ;
39725 this.toolbar.render(this.wrapEl, 'before');
39726 for(var i =0;i < ti.length;i++) {
39727 // Roo.log(['add child', items[i]]);
39728 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39730 this.toolbar.items = nitems;
39731 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39732 delete config.toolbar;
39736 // xtype created footer. - not sure if will work as we normally have to render first..
39737 if (this.footer && !this.footer.el && this.footer.xtype) {
39738 if (!this.wrapEl) {
39739 this.wrapEl = this.el.wrap();
39742 this.footer.container = this.wrapEl.createChild();
39744 this.footer = Roo.factory(this.footer, Roo);
39749 if(typeof config == "string"){
39750 this.title = config;
39752 Roo.apply(this, config);
39756 this.resizeEl = Roo.get(this.resizeEl, true);
39758 this.resizeEl = this.el;
39760 // handle view.xtype
39768 * Fires when this panel is activated.
39769 * @param {Roo.ContentPanel} this
39773 * @event deactivate
39774 * Fires when this panel is activated.
39775 * @param {Roo.ContentPanel} this
39777 "deactivate" : true,
39781 * Fires when this panel is resized if fitToFrame is true.
39782 * @param {Roo.ContentPanel} this
39783 * @param {Number} width The width after any component adjustments
39784 * @param {Number} height The height after any component adjustments
39790 * Fires when this tab is created
39791 * @param {Roo.ContentPanel} this
39802 if(this.autoScroll && !this.iframe){
39803 this.resizeEl.setStyle("overflow", "auto");
39805 // fix randome scrolling
39806 //this.el.on('scroll', function() {
39807 // Roo.log('fix random scolling');
39808 // this.scrollTo('top',0);
39811 content = content || this.content;
39813 this.setContent(content);
39815 if(config && config.url){
39816 this.setUrl(this.url, this.params, this.loadOnce);
39821 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39823 if (this.view && typeof(this.view.xtype) != 'undefined') {
39824 this.view.el = this.el.appendChild(document.createElement("div"));
39825 this.view = Roo.factory(this.view);
39826 this.view.render && this.view.render(false, '');
39830 this.fireEvent('render', this);
39833 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39843 setRegion : function(region){
39844 this.region = region;
39845 this.setActiveClass(region && !this.background);
39849 setActiveClass: function(state)
39852 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39853 this.el.setStyle('position','relative');
39855 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39856 this.el.setStyle('position', 'absolute');
39861 * Returns the toolbar for this Panel if one was configured.
39862 * @return {Roo.Toolbar}
39864 getToolbar : function(){
39865 return this.toolbar;
39868 setActiveState : function(active)
39870 this.active = active;
39871 this.setActiveClass(active);
39873 if(this.fireEvent("deactivate", this) === false){
39878 this.fireEvent("activate", this);
39882 * Updates this panel's element (not for iframe)
39883 * @param {String} content The new content
39884 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39886 setContent : function(content, loadScripts){
39891 this.el.update(content, loadScripts);
39894 ignoreResize : function(w, h){
39895 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39898 this.lastSize = {width: w, height: h};
39903 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39904 * @return {Roo.UpdateManager} The UpdateManager
39906 getUpdateManager : function(){
39910 return this.el.getUpdateManager();
39913 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39914 * Does not work with IFRAME contents
39915 * @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:
39918 url: "your-url.php",
39919 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39920 callback: yourFunction,
39921 scope: yourObject, //(optional scope)
39924 text: "Loading...",
39930 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39931 * 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.
39932 * @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}
39933 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39934 * @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.
39935 * @return {Roo.ContentPanel} this
39943 var um = this.el.getUpdateManager();
39944 um.update.apply(um, arguments);
39950 * 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.
39951 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39952 * @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)
39953 * @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)
39954 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39956 setUrl : function(url, params, loadOnce){
39958 this.iframeEl.dom.src = url;
39962 if(this.refreshDelegate){
39963 this.removeListener("activate", this.refreshDelegate);
39965 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39966 this.on("activate", this.refreshDelegate);
39967 return this.el.getUpdateManager();
39970 _handleRefresh : function(url, params, loadOnce){
39971 if(!loadOnce || !this.loaded){
39972 var updater = this.el.getUpdateManager();
39973 updater.update(url, params, this._setLoaded.createDelegate(this));
39977 _setLoaded : function(){
39978 this.loaded = true;
39982 * Returns this panel's id
39985 getId : function(){
39990 * Returns this panel's element - used by regiosn to add.
39991 * @return {Roo.Element}
39993 getEl : function(){
39994 return this.wrapEl || this.el;
39999 adjustForComponents : function(width, height)
40001 //Roo.log('adjustForComponents ');
40002 if(this.resizeEl != this.el){
40003 width -= this.el.getFrameWidth('lr');
40004 height -= this.el.getFrameWidth('tb');
40007 var te = this.toolbar.getEl();
40008 te.setWidth(width);
40009 height -= te.getHeight();
40012 var te = this.footer.getEl();
40013 te.setWidth(width);
40014 height -= te.getHeight();
40018 if(this.adjustments){
40019 width += this.adjustments[0];
40020 height += this.adjustments[1];
40022 return {"width": width, "height": height};
40025 setSize : function(width, height){
40026 if(this.fitToFrame && !this.ignoreResize(width, height)){
40027 if(this.fitContainer && this.resizeEl != this.el){
40028 this.el.setSize(width, height);
40030 var size = this.adjustForComponents(width, height);
40032 this.iframeEl.setSize(width,height);
40035 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40036 this.fireEvent('resize', this, size.width, size.height);
40043 * Returns this panel's title
40046 getTitle : function(){
40048 if (typeof(this.title) != 'object') {
40053 for (var k in this.title) {
40054 if (!this.title.hasOwnProperty(k)) {
40058 if (k.indexOf('-') >= 0) {
40059 var s = k.split('-');
40060 for (var i = 0; i<s.length; i++) {
40061 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40064 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40071 * Set this panel's title
40072 * @param {String} title
40074 setTitle : function(title){
40075 this.title = title;
40077 this.region.updatePanelTitle(this, title);
40082 * Returns true is this panel was configured to be closable
40083 * @return {Boolean}
40085 isClosable : function(){
40086 return this.closable;
40089 beforeSlide : function(){
40091 this.resizeEl.clip();
40094 afterSlide : function(){
40096 this.resizeEl.unclip();
40100 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40101 * Will fail silently if the {@link #setUrl} method has not been called.
40102 * This does not activate the panel, just updates its content.
40104 refresh : function(){
40105 if(this.refreshDelegate){
40106 this.loaded = false;
40107 this.refreshDelegate();
40112 * Destroys this panel
40114 destroy : function(){
40115 this.el.removeAllListeners();
40116 var tempEl = document.createElement("span");
40117 tempEl.appendChild(this.el.dom);
40118 tempEl.innerHTML = "";
40124 * form - if the content panel contains a form - this is a reference to it.
40125 * @type {Roo.form.Form}
40129 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40130 * This contains a reference to it.
40136 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40146 * @param {Object} cfg Xtype definition of item to add.
40150 getChildContainer: function () {
40151 return this.getEl();
40156 var ret = new Roo.factory(cfg);
40161 if (cfg.xtype.match(/^Form$/)) {
40164 //if (this.footer) {
40165 // el = this.footer.container.insertSibling(false, 'before');
40167 el = this.el.createChild();
40170 this.form = new Roo.form.Form(cfg);
40173 if ( this.form.allItems.length) {
40174 this.form.render(el.dom);
40178 // should only have one of theses..
40179 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40180 // views.. should not be just added - used named prop 'view''
40182 cfg.el = this.el.appendChild(document.createElement("div"));
40185 var ret = new Roo.factory(cfg);
40187 ret.render && ret.render(false, ''); // render blank..
40197 * @class Roo.bootstrap.panel.Grid
40198 * @extends Roo.bootstrap.panel.Content
40200 * Create a new GridPanel.
40201 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40202 * @param {Object} config A the config object
40208 Roo.bootstrap.panel.Grid = function(config)
40212 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40213 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40215 config.el = this.wrapper;
40216 //this.el = this.wrapper;
40218 if (config.container) {
40219 // ctor'ed from a Border/panel.grid
40222 this.wrapper.setStyle("overflow", "hidden");
40223 this.wrapper.addClass('roo-grid-container');
40228 if(config.toolbar){
40229 var tool_el = this.wrapper.createChild();
40230 this.toolbar = Roo.factory(config.toolbar);
40232 if (config.toolbar.items) {
40233 ti = config.toolbar.items ;
40234 delete config.toolbar.items ;
40238 this.toolbar.render(tool_el);
40239 for(var i =0;i < ti.length;i++) {
40240 // Roo.log(['add child', items[i]]);
40241 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40243 this.toolbar.items = nitems;
40245 delete config.toolbar;
40248 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40249 config.grid.scrollBody = true;;
40250 config.grid.monitorWindowResize = false; // turn off autosizing
40251 config.grid.autoHeight = false;
40252 config.grid.autoWidth = false;
40254 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40256 if (config.background) {
40257 // render grid on panel activation (if panel background)
40258 this.on('activate', function(gp) {
40259 if (!gp.grid.rendered) {
40260 gp.grid.render(this.wrapper);
40261 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40266 this.grid.render(this.wrapper);
40267 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40270 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40271 // ??? needed ??? config.el = this.wrapper;
40276 // xtype created footer. - not sure if will work as we normally have to render first..
40277 if (this.footer && !this.footer.el && this.footer.xtype) {
40279 var ctr = this.grid.getView().getFooterPanel(true);
40280 this.footer.dataSource = this.grid.dataSource;
40281 this.footer = Roo.factory(this.footer, Roo);
40282 this.footer.render(ctr);
40292 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40293 getId : function(){
40294 return this.grid.id;
40298 * Returns the grid for this panel
40299 * @return {Roo.bootstrap.Table}
40301 getGrid : function(){
40305 setSize : function(width, height){
40306 if(!this.ignoreResize(width, height)){
40307 var grid = this.grid;
40308 var size = this.adjustForComponents(width, height);
40309 // tfoot is not a footer?
40312 var gridel = grid.getGridEl();
40313 gridel.setSize(size.width, size.height);
40315 var tbd = grid.getGridEl().select('tbody', true).first();
40316 var thd = grid.getGridEl().select('thead',true).first();
40317 var tbf= grid.getGridEl().select('tfoot', true).first();
40320 size.height -= tbf.getHeight();
40323 size.height -= thd.getHeight();
40326 tbd.setSize(size.width, size.height );
40327 // this is for the account management tab -seems to work there.
40328 var thd = grid.getGridEl().select('thead',true).first();
40330 // tbd.setSize(size.width, size.height - thd.getHeight());
40339 beforeSlide : function(){
40340 this.grid.getView().scroller.clip();
40343 afterSlide : function(){
40344 this.grid.getView().scroller.unclip();
40347 destroy : function(){
40348 this.grid.destroy();
40350 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40355 * @class Roo.bootstrap.panel.Nest
40356 * @extends Roo.bootstrap.panel.Content
40358 * Create a new Panel, that can contain a layout.Border.
40361 * @param {Roo.BorderLayout} layout The layout for this panel
40362 * @param {String/Object} config A string to set only the title or a config object
40364 Roo.bootstrap.panel.Nest = function(config)
40366 // construct with only one argument..
40367 /* FIXME - implement nicer consturctors
40368 if (layout.layout) {
40370 layout = config.layout;
40371 delete config.layout;
40373 if (layout.xtype && !layout.getEl) {
40374 // then layout needs constructing..
40375 layout = Roo.factory(layout, Roo);
40379 config.el = config.layout.getEl();
40381 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40383 config.layout.monitorWindowResize = false; // turn off autosizing
40384 this.layout = config.layout;
40385 this.layout.getEl().addClass("roo-layout-nested-layout");
40386 this.layout.parent = this;
40393 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40395 setSize : function(width, height){
40396 if(!this.ignoreResize(width, height)){
40397 var size = this.adjustForComponents(width, height);
40398 var el = this.layout.getEl();
40399 if (size.height < 1) {
40400 el.setWidth(size.width);
40402 el.setSize(size.width, size.height);
40404 var touch = el.dom.offsetWidth;
40405 this.layout.layout();
40406 // ie requires a double layout on the first pass
40407 if(Roo.isIE && !this.initialized){
40408 this.initialized = true;
40409 this.layout.layout();
40414 // activate all subpanels if not currently active..
40416 setActiveState : function(active){
40417 this.active = active;
40418 this.setActiveClass(active);
40421 this.fireEvent("deactivate", this);
40425 this.fireEvent("activate", this);
40426 // not sure if this should happen before or after..
40427 if (!this.layout) {
40428 return; // should not happen..
40431 for (var r in this.layout.regions) {
40432 reg = this.layout.getRegion(r);
40433 if (reg.getActivePanel()) {
40434 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40435 reg.setActivePanel(reg.getActivePanel());
40438 if (!reg.panels.length) {
40441 reg.showPanel(reg.getPanel(0));
40450 * Returns the nested BorderLayout for this panel
40451 * @return {Roo.BorderLayout}
40453 getLayout : function(){
40454 return this.layout;
40458 * Adds a xtype elements to the layout of the nested panel
40462 xtype : 'ContentPanel',
40469 xtype : 'NestedLayoutPanel',
40475 items : [ ... list of content panels or nested layout panels.. ]
40479 * @param {Object} cfg Xtype definition of item to add.
40481 addxtype : function(cfg) {
40482 return this.layout.addxtype(cfg);
40487 * Ext JS Library 1.1.1
40488 * Copyright(c) 2006-2007, Ext JS, LLC.
40490 * Originally Released Under LGPL - original licence link has changed is not relivant.
40493 * <script type="text/javascript">
40496 * @class Roo.TabPanel
40497 * @extends Roo.util.Observable
40498 * A lightweight tab container.
40502 // basic tabs 1, built from existing content
40503 var tabs = new Roo.TabPanel("tabs1");
40504 tabs.addTab("script", "View Script");
40505 tabs.addTab("markup", "View Markup");
40506 tabs.activate("script");
40508 // more advanced tabs, built from javascript
40509 var jtabs = new Roo.TabPanel("jtabs");
40510 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40512 // set up the UpdateManager
40513 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40514 var updater = tab2.getUpdateManager();
40515 updater.setDefaultUrl("ajax1.htm");
40516 tab2.on('activate', updater.refresh, updater, true);
40518 // Use setUrl for Ajax loading
40519 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40520 tab3.setUrl("ajax2.htm", null, true);
40523 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40526 jtabs.activate("jtabs-1");
40529 * Create a new TabPanel.
40530 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40531 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40533 Roo.bootstrap.panel.Tabs = function(config){
40535 * The container element for this TabPanel.
40536 * @type Roo.Element
40538 this.el = Roo.get(config.el);
40541 if(typeof config == "boolean"){
40542 this.tabPosition = config ? "bottom" : "top";
40544 Roo.apply(this, config);
40548 if(this.tabPosition == "bottom"){
40549 // if tabs are at the bottom = create the body first.
40550 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40551 this.el.addClass("roo-tabs-bottom");
40553 // next create the tabs holders
40555 if (this.tabPosition == "west"){
40557 var reg = this.region; // fake it..
40559 if (!reg.mgr.parent) {
40562 reg = reg.mgr.parent.region;
40564 Roo.log("got nest?");
40566 if (reg.mgr.getRegion('west')) {
40567 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40568 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40569 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40570 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40571 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40579 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40580 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40581 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40582 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40587 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40590 // finally - if tabs are at the top, then create the body last..
40591 if(this.tabPosition != "bottom"){
40592 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40593 * @type Roo.Element
40595 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40596 this.el.addClass("roo-tabs-top");
40600 this.bodyEl.setStyle("position", "relative");
40602 this.active = null;
40603 this.activateDelegate = this.activate.createDelegate(this);
40608 * Fires when the active tab changes
40609 * @param {Roo.TabPanel} this
40610 * @param {Roo.TabPanelItem} activePanel The new active tab
40614 * @event beforetabchange
40615 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40616 * @param {Roo.TabPanel} this
40617 * @param {Object} e Set cancel to true on this object to cancel the tab change
40618 * @param {Roo.TabPanelItem} tab The tab being changed to
40620 "beforetabchange" : true
40623 Roo.EventManager.onWindowResize(this.onResize, this);
40624 this.cpad = this.el.getPadding("lr");
40625 this.hiddenCount = 0;
40628 // toolbar on the tabbar support...
40629 if (this.toolbar) {
40630 alert("no toolbar support yet");
40631 this.toolbar = false;
40633 var tcfg = this.toolbar;
40634 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40635 this.toolbar = new Roo.Toolbar(tcfg);
40636 if (Roo.isSafari) {
40637 var tbl = tcfg.container.child('table', true);
40638 tbl.setAttribute('width', '100%');
40646 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40649 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40651 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40653 tabPosition : "top",
40655 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40657 currentTabWidth : 0,
40659 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40663 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40667 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40669 preferredTabWidth : 175,
40671 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40673 resizeTabs : false,
40675 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40677 monitorResize : true,
40679 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40681 toolbar : false, // set by caller..
40683 region : false, /// set by caller
40685 disableTooltips : true, // not used yet...
40688 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40689 * @param {String} id The id of the div to use <b>or create</b>
40690 * @param {String} text The text for the tab
40691 * @param {String} content (optional) Content to put in the TabPanelItem body
40692 * @param {Boolean} closable (optional) True to create a close icon on the tab
40693 * @return {Roo.TabPanelItem} The created TabPanelItem
40695 addTab : function(id, text, content, closable, tpl)
40697 var item = new Roo.bootstrap.panel.TabItem({
40701 closable : closable,
40704 this.addTabItem(item);
40706 item.setContent(content);
40712 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40713 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40714 * @return {Roo.TabPanelItem}
40716 getTab : function(id){
40717 return this.items[id];
40721 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40722 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40724 hideTab : function(id){
40725 var t = this.items[id];
40728 this.hiddenCount++;
40729 this.autoSizeTabs();
40734 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40735 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40737 unhideTab : function(id){
40738 var t = this.items[id];
40740 t.setHidden(false);
40741 this.hiddenCount--;
40742 this.autoSizeTabs();
40747 * Adds an existing {@link Roo.TabPanelItem}.
40748 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40750 addTabItem : function(item)
40752 this.items[item.id] = item;
40753 this.items.push(item);
40754 this.autoSizeTabs();
40755 // if(this.resizeTabs){
40756 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40757 // this.autoSizeTabs();
40759 // item.autoSize();
40764 * Removes a {@link Roo.TabPanelItem}.
40765 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40767 removeTab : function(id){
40768 var items = this.items;
40769 var tab = items[id];
40770 if(!tab) { return; }
40771 var index = items.indexOf(tab);
40772 if(this.active == tab && items.length > 1){
40773 var newTab = this.getNextAvailable(index);
40778 this.stripEl.dom.removeChild(tab.pnode.dom);
40779 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40780 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40782 items.splice(index, 1);
40783 delete this.items[tab.id];
40784 tab.fireEvent("close", tab);
40785 tab.purgeListeners();
40786 this.autoSizeTabs();
40789 getNextAvailable : function(start){
40790 var items = this.items;
40792 // look for a next tab that will slide over to
40793 // replace the one being removed
40794 while(index < items.length){
40795 var item = items[++index];
40796 if(item && !item.isHidden()){
40800 // if one isn't found select the previous tab (on the left)
40803 var item = items[--index];
40804 if(item && !item.isHidden()){
40812 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40813 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40815 disableTab : function(id){
40816 var tab = this.items[id];
40817 if(tab && this.active != tab){
40823 * Enables a {@link Roo.TabPanelItem} that is disabled.
40824 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40826 enableTab : function(id){
40827 var tab = this.items[id];
40832 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40833 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40834 * @return {Roo.TabPanelItem} The TabPanelItem.
40836 activate : function(id)
40838 //Roo.log('activite:' + id);
40840 var tab = this.items[id];
40844 if(tab == this.active || tab.disabled){
40848 this.fireEvent("beforetabchange", this, e, tab);
40849 if(e.cancel !== true && !tab.disabled){
40851 this.active.hide();
40853 this.active = this.items[id];
40854 this.active.show();
40855 this.fireEvent("tabchange", this, this.active);
40861 * Gets the active {@link Roo.TabPanelItem}.
40862 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40864 getActiveTab : function(){
40865 return this.active;
40869 * Updates the tab body element to fit the height of the container element
40870 * for overflow scrolling
40871 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40873 syncHeight : function(targetHeight){
40874 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40875 var bm = this.bodyEl.getMargins();
40876 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40877 this.bodyEl.setHeight(newHeight);
40881 onResize : function(){
40882 if(this.monitorResize){
40883 this.autoSizeTabs();
40888 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40890 beginUpdate : function(){
40891 this.updating = true;
40895 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40897 endUpdate : function(){
40898 this.updating = false;
40899 this.autoSizeTabs();
40903 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40905 autoSizeTabs : function()
40907 var count = this.items.length;
40908 var vcount = count - this.hiddenCount;
40911 this.stripEl.hide();
40913 this.stripEl.show();
40916 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40921 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40922 var availWidth = Math.floor(w / vcount);
40923 var b = this.stripBody;
40924 if(b.getWidth() > w){
40925 var tabs = this.items;
40926 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40927 if(availWidth < this.minTabWidth){
40928 /*if(!this.sleft){ // incomplete scrolling code
40929 this.createScrollButtons();
40932 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40935 if(this.currentTabWidth < this.preferredTabWidth){
40936 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40942 * Returns the number of tabs in this TabPanel.
40945 getCount : function(){
40946 return this.items.length;
40950 * Resizes all the tabs to the passed width
40951 * @param {Number} The new width
40953 setTabWidth : function(width){
40954 this.currentTabWidth = width;
40955 for(var i = 0, len = this.items.length; i < len; i++) {
40956 if(!this.items[i].isHidden()) {
40957 this.items[i].setWidth(width);
40963 * Destroys this TabPanel
40964 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40966 destroy : function(removeEl){
40967 Roo.EventManager.removeResizeListener(this.onResize, this);
40968 for(var i = 0, len = this.items.length; i < len; i++){
40969 this.items[i].purgeListeners();
40971 if(removeEl === true){
40972 this.el.update("");
40977 createStrip : function(container)
40979 var strip = document.createElement("nav");
40980 strip.className = Roo.bootstrap.version == 4 ?
40981 "navbar-light bg-light" :
40982 "navbar navbar-default"; //"x-tabs-wrap";
40983 container.appendChild(strip);
40987 createStripList : function(strip)
40989 // div wrapper for retard IE
40990 // returns the "tr" element.
40991 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40992 //'<div class="x-tabs-strip-wrap">'+
40993 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40994 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40995 return strip.firstChild; //.firstChild.firstChild.firstChild;
40997 createBody : function(container)
40999 var body = document.createElement("div");
41000 Roo.id(body, "tab-body");
41001 //Roo.fly(body).addClass("x-tabs-body");
41002 Roo.fly(body).addClass("tab-content");
41003 container.appendChild(body);
41006 createItemBody :function(bodyEl, id){
41007 var body = Roo.getDom(id);
41009 body = document.createElement("div");
41012 //Roo.fly(body).addClass("x-tabs-item-body");
41013 Roo.fly(body).addClass("tab-pane");
41014 bodyEl.insertBefore(body, bodyEl.firstChild);
41018 createStripElements : function(stripEl, text, closable, tpl)
41020 var td = document.createElement("li"); // was td..
41021 td.className = 'nav-item';
41023 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41026 stripEl.appendChild(td);
41028 td.className = "x-tabs-closable";
41029 if(!this.closeTpl){
41030 this.closeTpl = new Roo.Template(
41031 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41032 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41033 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41036 var el = this.closeTpl.overwrite(td, {"text": text});
41037 var close = el.getElementsByTagName("div")[0];
41038 var inner = el.getElementsByTagName("em")[0];
41039 return {"el": el, "close": close, "inner": inner};
41042 // not sure what this is..
41043 // if(!this.tabTpl){
41044 //this.tabTpl = new Roo.Template(
41045 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41046 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41048 // this.tabTpl = new Roo.Template(
41049 // '<a href="#">' +
41050 // '<span unselectable="on"' +
41051 // (this.disableTooltips ? '' : ' title="{text}"') +
41052 // ' >{text}</span></a>'
41058 var template = tpl || this.tabTpl || false;
41061 template = new Roo.Template(
41062 Roo.bootstrap.version == 4 ?
41064 '<a class="nav-link" href="#" unselectable="on"' +
41065 (this.disableTooltips ? '' : ' title="{text}"') +
41068 '<a class="nav-link" href="#">' +
41069 '<span unselectable="on"' +
41070 (this.disableTooltips ? '' : ' title="{text}"') +
41071 ' >{text}</span></a>'
41076 switch (typeof(template)) {
41080 template = new Roo.Template(template);
41086 var el = template.overwrite(td, {"text": text});
41088 var inner = el.getElementsByTagName("span")[0];
41090 return {"el": el, "inner": inner};
41098 * @class Roo.TabPanelItem
41099 * @extends Roo.util.Observable
41100 * Represents an individual item (tab plus body) in a TabPanel.
41101 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41102 * @param {String} id The id of this TabPanelItem
41103 * @param {String} text The text for the tab of this TabPanelItem
41104 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41106 Roo.bootstrap.panel.TabItem = function(config){
41108 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41109 * @type Roo.TabPanel
41111 this.tabPanel = config.panel;
41113 * The id for this TabPanelItem
41116 this.id = config.id;
41118 this.disabled = false;
41120 this.text = config.text;
41122 this.loaded = false;
41123 this.closable = config.closable;
41126 * The body element for this TabPanelItem.
41127 * @type Roo.Element
41129 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41130 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41131 this.bodyEl.setStyle("display", "block");
41132 this.bodyEl.setStyle("zoom", "1");
41133 //this.hideAction();
41135 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41137 this.el = Roo.get(els.el);
41138 this.inner = Roo.get(els.inner, true);
41139 this.textEl = Roo.bootstrap.version == 4 ?
41140 this.el : Roo.get(this.el.dom.firstChild, true);
41142 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41143 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41146 // this.el.on("mousedown", this.onTabMouseDown, this);
41147 this.el.on("click", this.onTabClick, this);
41149 if(config.closable){
41150 var c = Roo.get(els.close, true);
41151 c.dom.title = this.closeText;
41152 c.addClassOnOver("close-over");
41153 c.on("click", this.closeClick, this);
41159 * Fires when this tab becomes the active tab.
41160 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41161 * @param {Roo.TabPanelItem} this
41165 * @event beforeclose
41166 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41167 * @param {Roo.TabPanelItem} this
41168 * @param {Object} e Set cancel to true on this object to cancel the close.
41170 "beforeclose": true,
41173 * Fires when this tab is closed.
41174 * @param {Roo.TabPanelItem} this
41178 * @event deactivate
41179 * Fires when this tab is no longer the active tab.
41180 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41181 * @param {Roo.TabPanelItem} this
41183 "deactivate" : true
41185 this.hidden = false;
41187 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41190 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41192 purgeListeners : function(){
41193 Roo.util.Observable.prototype.purgeListeners.call(this);
41194 this.el.removeAllListeners();
41197 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41200 this.status_node.addClass("active");
41203 this.tabPanel.stripWrap.repaint();
41205 this.fireEvent("activate", this.tabPanel, this);
41209 * Returns true if this tab is the active tab.
41210 * @return {Boolean}
41212 isActive : function(){
41213 return this.tabPanel.getActiveTab() == this;
41217 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41220 this.status_node.removeClass("active");
41222 this.fireEvent("deactivate", this.tabPanel, this);
41225 hideAction : function(){
41226 this.bodyEl.hide();
41227 this.bodyEl.setStyle("position", "absolute");
41228 this.bodyEl.setLeft("-20000px");
41229 this.bodyEl.setTop("-20000px");
41232 showAction : function(){
41233 this.bodyEl.setStyle("position", "relative");
41234 this.bodyEl.setTop("");
41235 this.bodyEl.setLeft("");
41236 this.bodyEl.show();
41240 * Set the tooltip for the tab.
41241 * @param {String} tooltip The tab's tooltip
41243 setTooltip : function(text){
41244 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41245 this.textEl.dom.qtip = text;
41246 this.textEl.dom.removeAttribute('title');
41248 this.textEl.dom.title = text;
41252 onTabClick : function(e){
41253 e.preventDefault();
41254 this.tabPanel.activate(this.id);
41257 onTabMouseDown : function(e){
41258 e.preventDefault();
41259 this.tabPanel.activate(this.id);
41262 getWidth : function(){
41263 return this.inner.getWidth();
41266 setWidth : function(width){
41267 var iwidth = width - this.linode.getPadding("lr");
41268 this.inner.setWidth(iwidth);
41269 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41270 this.linode.setWidth(width);
41274 * Show or hide the tab
41275 * @param {Boolean} hidden True to hide or false to show.
41277 setHidden : function(hidden){
41278 this.hidden = hidden;
41279 this.linode.setStyle("display", hidden ? "none" : "");
41283 * Returns true if this tab is "hidden"
41284 * @return {Boolean}
41286 isHidden : function(){
41287 return this.hidden;
41291 * Returns the text for this tab
41294 getText : function(){
41298 autoSize : function(){
41299 //this.el.beginMeasure();
41300 this.textEl.setWidth(1);
41302 * #2804 [new] Tabs in Roojs
41303 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41305 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41306 //this.el.endMeasure();
41310 * Sets the text for the tab (Note: this also sets the tooltip text)
41311 * @param {String} text The tab's text and tooltip
41313 setText : function(text){
41315 this.textEl.update(text);
41316 this.setTooltip(text);
41317 //if(!this.tabPanel.resizeTabs){
41318 // this.autoSize();
41322 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41324 activate : function(){
41325 this.tabPanel.activate(this.id);
41329 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41331 disable : function(){
41332 if(this.tabPanel.active != this){
41333 this.disabled = true;
41334 this.status_node.addClass("disabled");
41339 * Enables this TabPanelItem if it was previously disabled.
41341 enable : function(){
41342 this.disabled = false;
41343 this.status_node.removeClass("disabled");
41347 * Sets the content for this TabPanelItem.
41348 * @param {String} content The content
41349 * @param {Boolean} loadScripts true to look for and load scripts
41351 setContent : function(content, loadScripts){
41352 this.bodyEl.update(content, loadScripts);
41356 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41357 * @return {Roo.UpdateManager} The UpdateManager
41359 getUpdateManager : function(){
41360 return this.bodyEl.getUpdateManager();
41364 * Set a URL to be used to load the content for this TabPanelItem.
41365 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41366 * @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)
41367 * @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)
41368 * @return {Roo.UpdateManager} The UpdateManager
41370 setUrl : function(url, params, loadOnce){
41371 if(this.refreshDelegate){
41372 this.un('activate', this.refreshDelegate);
41374 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41375 this.on("activate", this.refreshDelegate);
41376 return this.bodyEl.getUpdateManager();
41380 _handleRefresh : function(url, params, loadOnce){
41381 if(!loadOnce || !this.loaded){
41382 var updater = this.bodyEl.getUpdateManager();
41383 updater.update(url, params, this._setLoaded.createDelegate(this));
41388 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41389 * Will fail silently if the setUrl method has not been called.
41390 * This does not activate the panel, just updates its content.
41392 refresh : function(){
41393 if(this.refreshDelegate){
41394 this.loaded = false;
41395 this.refreshDelegate();
41400 _setLoaded : function(){
41401 this.loaded = true;
41405 closeClick : function(e){
41408 this.fireEvent("beforeclose", this, o);
41409 if(o.cancel !== true){
41410 this.tabPanel.removeTab(this.id);
41414 * The text displayed in the tooltip for the close icon.
41417 closeText : "Close this tab"
41420 * This script refer to:
41421 * Title: International Telephone Input
41422 * Author: Jack O'Connor
41423 * Code version: v12.1.12
41424 * Availability: https://github.com/jackocnr/intl-tel-input.git
41427 Roo.bootstrap.PhoneInputData = function() {
41430 "Afghanistan (افغانستان)",
41435 "Albania (Shqipëri)",
41440 "Algeria (الجزائر)",
41465 "Antigua and Barbuda",
41475 "Armenia (Հայաստան)",
41491 "Austria (Österreich)",
41496 "Azerbaijan (Azərbaycan)",
41506 "Bahrain (البحرين)",
41511 "Bangladesh (বাংলাদেশ)",
41521 "Belarus (Беларусь)",
41526 "Belgium (België)",
41556 "Bosnia and Herzegovina (Босна и Херцеговина)",
41571 "British Indian Ocean Territory",
41576 "British Virgin Islands",
41586 "Bulgaria (България)",
41596 "Burundi (Uburundi)",
41601 "Cambodia (កម្ពុជា)",
41606 "Cameroon (Cameroun)",
41615 ["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"]
41618 "Cape Verde (Kabu Verdi)",
41623 "Caribbean Netherlands",
41634 "Central African Republic (République centrafricaine)",
41654 "Christmas Island",
41660 "Cocos (Keeling) Islands",
41671 "Comoros (جزر القمر)",
41676 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41681 "Congo (Republic) (Congo-Brazzaville)",
41701 "Croatia (Hrvatska)",
41722 "Czech Republic (Česká republika)",
41727 "Denmark (Danmark)",
41742 "Dominican Republic (República Dominicana)",
41746 ["809", "829", "849"]
41764 "Equatorial Guinea (Guinea Ecuatorial)",
41784 "Falkland Islands (Islas Malvinas)",
41789 "Faroe Islands (Føroyar)",
41810 "French Guiana (Guyane française)",
41815 "French Polynesia (Polynésie française)",
41830 "Georgia (საქართველო)",
41835 "Germany (Deutschland)",
41855 "Greenland (Kalaallit Nunaat)",
41892 "Guinea-Bissau (Guiné Bissau)",
41917 "Hungary (Magyarország)",
41922 "Iceland (Ísland)",
41942 "Iraq (العراق)",
41958 "Israel (ישראל)",
41985 "Jordan (الأردن)",
41990 "Kazakhstan (Казахстан)",
42011 "Kuwait (الكويت)",
42016 "Kyrgyzstan (Кыргызстан)",
42026 "Latvia (Latvija)",
42031 "Lebanon (لبنان)",
42046 "Libya (ليبيا)",
42056 "Lithuania (Lietuva)",
42071 "Macedonia (FYROM) (Македонија)",
42076 "Madagascar (Madagasikara)",
42106 "Marshall Islands",
42116 "Mauritania (موريتانيا)",
42121 "Mauritius (Moris)",
42142 "Moldova (Republica Moldova)",
42152 "Mongolia (Монгол)",
42157 "Montenegro (Crna Gora)",
42167 "Morocco (المغرب)",
42173 "Mozambique (Moçambique)",
42178 "Myanmar (Burma) (မြန်မာ)",
42183 "Namibia (Namibië)",
42198 "Netherlands (Nederland)",
42203 "New Caledonia (Nouvelle-Calédonie)",
42238 "North Korea (조선 민주주의 인민 공화국)",
42243 "Northern Mariana Islands",
42259 "Pakistan (پاکستان)",
42269 "Palestine (فلسطين)",
42279 "Papua New Guinea",
42321 "Réunion (La Réunion)",
42327 "Romania (România)",
42343 "Saint Barthélemy",
42354 "Saint Kitts and Nevis",
42364 "Saint Martin (Saint-Martin (partie française))",
42370 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42375 "Saint Vincent and the Grenadines",
42390 "São Tomé and Príncipe (São Tomé e Príncipe)",
42395 "Saudi Arabia (المملكة العربية السعودية)",
42400 "Senegal (Sénégal)",
42430 "Slovakia (Slovensko)",
42435 "Slovenia (Slovenija)",
42445 "Somalia (Soomaaliya)",
42455 "South Korea (대한민국)",
42460 "South Sudan (جنوب السودان)",
42470 "Sri Lanka (ශ්රී ලංකාව)",
42475 "Sudan (السودان)",
42485 "Svalbard and Jan Mayen",
42496 "Sweden (Sverige)",
42501 "Switzerland (Schweiz)",
42506 "Syria (سوريا)",
42551 "Trinidad and Tobago",
42556 "Tunisia (تونس)",
42561 "Turkey (Türkiye)",
42571 "Turks and Caicos Islands",
42581 "U.S. Virgin Islands",
42591 "Ukraine (Україна)",
42596 "United Arab Emirates (الإمارات العربية المتحدة)",
42618 "Uzbekistan (Oʻzbekiston)",
42628 "Vatican City (Città del Vaticano)",
42639 "Vietnam (Việt Nam)",
42644 "Wallis and Futuna (Wallis-et-Futuna)",
42649 "Western Sahara (الصحراء الغربية)",
42655 "Yemen (اليمن)",
42679 * This script refer to:
42680 * Title: International Telephone Input
42681 * Author: Jack O'Connor
42682 * Code version: v12.1.12
42683 * Availability: https://github.com/jackocnr/intl-tel-input.git
42687 * @class Roo.bootstrap.PhoneInput
42688 * @extends Roo.bootstrap.TriggerField
42689 * An input with International dial-code selection
42691 * @cfg {String} defaultDialCode default '+852'
42692 * @cfg {Array} preferedCountries default []
42695 * Create a new PhoneInput.
42696 * @param {Object} config Configuration options
42699 Roo.bootstrap.PhoneInput = function(config) {
42700 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42703 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42705 listWidth: undefined,
42707 selectedClass: 'active',
42709 invalidClass : "has-warning",
42711 validClass: 'has-success',
42713 allowed: '0123456789',
42718 * @cfg {String} defaultDialCode The default dial code when initializing the input
42720 defaultDialCode: '+852',
42723 * @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
42725 preferedCountries: false,
42727 getAutoCreate : function()
42729 var data = Roo.bootstrap.PhoneInputData();
42730 var align = this.labelAlign || this.parentLabelAlign();
42733 this.allCountries = [];
42734 this.dialCodeMapping = [];
42736 for (var i = 0; i < data.length; i++) {
42738 this.allCountries[i] = {
42742 priority: c[3] || 0,
42743 areaCodes: c[4] || null
42745 this.dialCodeMapping[c[2]] = {
42748 priority: c[3] || 0,
42749 areaCodes: c[4] || null
42761 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42762 maxlength: this.max_length,
42763 cls : 'form-control tel-input',
42764 autocomplete: 'new-password'
42767 var hiddenInput = {
42770 cls: 'hidden-tel-input'
42774 hiddenInput.name = this.name;
42777 if (this.disabled) {
42778 input.disabled = true;
42781 var flag_container = {
42798 cls: this.hasFeedback ? 'has-feedback' : '',
42804 cls: 'dial-code-holder',
42811 cls: 'roo-select2-container input-group',
42818 if (this.fieldLabel.length) {
42821 tooltip: 'This field is required'
42827 cls: 'control-label',
42833 html: this.fieldLabel
42836 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42842 if(this.indicatorpos == 'right') {
42843 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42850 if(align == 'left') {
42858 if(this.labelWidth > 12){
42859 label.style = "width: " + this.labelWidth + 'px';
42861 if(this.labelWidth < 13 && this.labelmd == 0){
42862 this.labelmd = this.labelWidth;
42864 if(this.labellg > 0){
42865 label.cls += ' col-lg-' + this.labellg;
42866 input.cls += ' col-lg-' + (12 - this.labellg);
42868 if(this.labelmd > 0){
42869 label.cls += ' col-md-' + this.labelmd;
42870 container.cls += ' col-md-' + (12 - this.labelmd);
42872 if(this.labelsm > 0){
42873 label.cls += ' col-sm-' + this.labelsm;
42874 container.cls += ' col-sm-' + (12 - this.labelsm);
42876 if(this.labelxs > 0){
42877 label.cls += ' col-xs-' + this.labelxs;
42878 container.cls += ' col-xs-' + (12 - this.labelxs);
42888 var settings = this;
42890 ['xs','sm','md','lg'].map(function(size){
42891 if (settings[size]) {
42892 cfg.cls += ' col-' + size + '-' + settings[size];
42896 this.store = new Roo.data.Store({
42897 proxy : new Roo.data.MemoryProxy({}),
42898 reader : new Roo.data.JsonReader({
42909 'name' : 'dialCode',
42913 'name' : 'priority',
42917 'name' : 'areaCodes',
42924 if(!this.preferedCountries) {
42925 this.preferedCountries = [
42932 var p = this.preferedCountries.reverse();
42935 for (var i = 0; i < p.length; i++) {
42936 for (var j = 0; j < this.allCountries.length; j++) {
42937 if(this.allCountries[j].iso2 == p[i]) {
42938 var t = this.allCountries[j];
42939 this.allCountries.splice(j,1);
42940 this.allCountries.unshift(t);
42946 this.store.proxy.data = {
42948 data: this.allCountries
42954 initEvents : function()
42957 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42959 this.indicator = this.indicatorEl();
42960 this.flag = this.flagEl();
42961 this.dialCodeHolder = this.dialCodeHolderEl();
42963 this.trigger = this.el.select('div.flag-box',true).first();
42964 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42969 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42970 _this.list.setWidth(lw);
42973 this.list.on('mouseover', this.onViewOver, this);
42974 this.list.on('mousemove', this.onViewMove, this);
42975 this.inputEl().on("keyup", this.onKeyUp, this);
42976 this.inputEl().on("keypress", this.onKeyPress, this);
42978 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42980 this.view = new Roo.View(this.list, this.tpl, {
42981 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42984 this.view.on('click', this.onViewClick, this);
42985 this.setValue(this.defaultDialCode);
42988 onTriggerClick : function(e)
42990 Roo.log('trigger click');
42995 if(this.isExpanded()){
42997 this.hasFocus = false;
42999 this.store.load({});
43000 this.hasFocus = true;
43005 isExpanded : function()
43007 return this.list.isVisible();
43010 collapse : function()
43012 if(!this.isExpanded()){
43016 Roo.get(document).un('mousedown', this.collapseIf, this);
43017 Roo.get(document).un('mousewheel', this.collapseIf, this);
43018 this.fireEvent('collapse', this);
43022 expand : function()
43026 if(this.isExpanded() || !this.hasFocus){
43030 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43031 this.list.setWidth(lw);
43034 this.restrictHeight();
43036 Roo.get(document).on('mousedown', this.collapseIf, this);
43037 Roo.get(document).on('mousewheel', this.collapseIf, this);
43039 this.fireEvent('expand', this);
43042 restrictHeight : function()
43044 this.list.alignTo(this.inputEl(), this.listAlign);
43045 this.list.alignTo(this.inputEl(), this.listAlign);
43048 onViewOver : function(e, t)
43050 if(this.inKeyMode){
43053 var item = this.view.findItemFromChild(t);
43056 var index = this.view.indexOf(item);
43057 this.select(index, false);
43062 onViewClick : function(view, doFocus, el, e)
43064 var index = this.view.getSelectedIndexes()[0];
43066 var r = this.store.getAt(index);
43069 this.onSelect(r, index);
43071 if(doFocus !== false && !this.blockFocus){
43072 this.inputEl().focus();
43076 onViewMove : function(e, t)
43078 this.inKeyMode = false;
43081 select : function(index, scrollIntoView)
43083 this.selectedIndex = index;
43084 this.view.select(index);
43085 if(scrollIntoView !== false){
43086 var el = this.view.getNode(index);
43088 this.list.scrollChildIntoView(el, false);
43093 createList : function()
43095 this.list = Roo.get(document.body).createChild({
43097 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43098 style: 'display:none'
43101 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43104 collapseIf : function(e)
43106 var in_combo = e.within(this.el);
43107 var in_list = e.within(this.list);
43108 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43110 if (in_combo || in_list || is_list) {
43116 onSelect : function(record, index)
43118 if(this.fireEvent('beforeselect', this, record, index) !== false){
43120 this.setFlagClass(record.data.iso2);
43121 this.setDialCode(record.data.dialCode);
43122 this.hasFocus = false;
43124 this.fireEvent('select', this, record, index);
43128 flagEl : function()
43130 var flag = this.el.select('div.flag',true).first();
43137 dialCodeHolderEl : function()
43139 var d = this.el.select('input.dial-code-holder',true).first();
43146 setDialCode : function(v)
43148 this.dialCodeHolder.dom.value = '+'+v;
43151 setFlagClass : function(n)
43153 this.flag.dom.className = 'flag '+n;
43156 getValue : function()
43158 var v = this.inputEl().getValue();
43159 if(this.dialCodeHolder) {
43160 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43165 setValue : function(v)
43167 var d = this.getDialCode(v);
43169 //invalid dial code
43170 if(v.length == 0 || !d || d.length == 0) {
43172 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43173 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43179 this.setFlagClass(this.dialCodeMapping[d].iso2);
43180 this.setDialCode(d);
43181 this.inputEl().dom.value = v.replace('+'+d,'');
43182 this.hiddenEl().dom.value = this.getValue();
43187 getDialCode : function(v)
43191 if (v.length == 0) {
43192 return this.dialCodeHolder.dom.value;
43196 if (v.charAt(0) != "+") {
43199 var numericChars = "";
43200 for (var i = 1; i < v.length; i++) {
43201 var c = v.charAt(i);
43204 if (this.dialCodeMapping[numericChars]) {
43205 dialCode = v.substr(1, i);
43207 if (numericChars.length == 4) {
43217 this.setValue(this.defaultDialCode);
43221 hiddenEl : function()
43223 return this.el.select('input.hidden-tel-input',true).first();
43226 // after setting val
43227 onKeyUp : function(e){
43228 this.setValue(this.getValue());
43231 onKeyPress : function(e){
43232 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43239 * @class Roo.bootstrap.MoneyField
43240 * @extends Roo.bootstrap.ComboBox
43241 * Bootstrap MoneyField class
43244 * Create a new MoneyField.
43245 * @param {Object} config Configuration options
43248 Roo.bootstrap.MoneyField = function(config) {
43250 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43254 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43257 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43259 allowDecimals : true,
43261 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43263 decimalSeparator : ".",
43265 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43267 decimalPrecision : 0,
43269 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43271 allowNegative : true,
43273 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43277 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43279 minValue : Number.NEGATIVE_INFINITY,
43281 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43283 maxValue : Number.MAX_VALUE,
43285 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43287 minText : "The minimum value for this field is {0}",
43289 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43291 maxText : "The maximum value for this field is {0}",
43293 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43294 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43296 nanText : "{0} is not a valid number",
43298 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43302 * @cfg {String} defaults currency of the MoneyField
43303 * value should be in lkey
43305 defaultCurrency : false,
43307 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43309 thousandsDelimiter : false,
43311 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43322 getAutoCreate : function()
43324 var align = this.labelAlign || this.parentLabelAlign();
43336 cls : 'form-control roo-money-amount-input',
43337 autocomplete: 'new-password'
43340 var hiddenInput = {
43344 cls: 'hidden-number-input'
43347 if(this.max_length) {
43348 input.maxlength = this.max_length;
43352 hiddenInput.name = this.name;
43355 if (this.disabled) {
43356 input.disabled = true;
43359 var clg = 12 - this.inputlg;
43360 var cmd = 12 - this.inputmd;
43361 var csm = 12 - this.inputsm;
43362 var cxs = 12 - this.inputxs;
43366 cls : 'row roo-money-field',
43370 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43374 cls: 'roo-select2-container input-group',
43378 cls : 'form-control roo-money-currency-input',
43379 autocomplete: 'new-password',
43381 name : this.currencyName
43385 cls : 'input-group-addon',
43399 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43403 cls: this.hasFeedback ? 'has-feedback' : '',
43414 if (this.fieldLabel.length) {
43417 tooltip: 'This field is required'
43423 cls: 'control-label',
43429 html: this.fieldLabel
43432 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43438 if(this.indicatorpos == 'right') {
43439 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43446 if(align == 'left') {
43454 if(this.labelWidth > 12){
43455 label.style = "width: " + this.labelWidth + 'px';
43457 if(this.labelWidth < 13 && this.labelmd == 0){
43458 this.labelmd = this.labelWidth;
43460 if(this.labellg > 0){
43461 label.cls += ' col-lg-' + this.labellg;
43462 input.cls += ' col-lg-' + (12 - this.labellg);
43464 if(this.labelmd > 0){
43465 label.cls += ' col-md-' + this.labelmd;
43466 container.cls += ' col-md-' + (12 - this.labelmd);
43468 if(this.labelsm > 0){
43469 label.cls += ' col-sm-' + this.labelsm;
43470 container.cls += ' col-sm-' + (12 - this.labelsm);
43472 if(this.labelxs > 0){
43473 label.cls += ' col-xs-' + this.labelxs;
43474 container.cls += ' col-xs-' + (12 - this.labelxs);
43485 var settings = this;
43487 ['xs','sm','md','lg'].map(function(size){
43488 if (settings[size]) {
43489 cfg.cls += ' col-' + size + '-' + settings[size];
43496 initEvents : function()
43498 this.indicator = this.indicatorEl();
43500 this.initCurrencyEvent();
43502 this.initNumberEvent();
43505 initCurrencyEvent : function()
43508 throw "can not find store for combo";
43511 this.store = Roo.factory(this.store, Roo.data);
43512 this.store.parent = this;
43516 this.triggerEl = this.el.select('.input-group-addon', true).first();
43518 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43523 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43524 _this.list.setWidth(lw);
43527 this.list.on('mouseover', this.onViewOver, this);
43528 this.list.on('mousemove', this.onViewMove, this);
43529 this.list.on('scroll', this.onViewScroll, this);
43532 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43535 this.view = new Roo.View(this.list, this.tpl, {
43536 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43539 this.view.on('click', this.onViewClick, this);
43541 this.store.on('beforeload', this.onBeforeLoad, this);
43542 this.store.on('load', this.onLoad, this);
43543 this.store.on('loadexception', this.onLoadException, this);
43545 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43546 "up" : function(e){
43547 this.inKeyMode = true;
43551 "down" : function(e){
43552 if(!this.isExpanded()){
43553 this.onTriggerClick();
43555 this.inKeyMode = true;
43560 "enter" : function(e){
43563 if(this.fireEvent("specialkey", this, e)){
43564 this.onViewClick(false);
43570 "esc" : function(e){
43574 "tab" : function(e){
43577 if(this.fireEvent("specialkey", this, e)){
43578 this.onViewClick(false);
43586 doRelay : function(foo, bar, hname){
43587 if(hname == 'down' || this.scope.isExpanded()){
43588 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43596 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43600 initNumberEvent : function(e)
43602 this.inputEl().on("keydown" , this.fireKey, this);
43603 this.inputEl().on("focus", this.onFocus, this);
43604 this.inputEl().on("blur", this.onBlur, this);
43606 this.inputEl().relayEvent('keyup', this);
43608 if(this.indicator){
43609 this.indicator.addClass('invisible');
43612 this.originalValue = this.getValue();
43614 if(this.validationEvent == 'keyup'){
43615 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43616 this.inputEl().on('keyup', this.filterValidation, this);
43618 else if(this.validationEvent !== false){
43619 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43622 if(this.selectOnFocus){
43623 this.on("focus", this.preFocus, this);
43626 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43627 this.inputEl().on("keypress", this.filterKeys, this);
43629 this.inputEl().relayEvent('keypress', this);
43632 var allowed = "0123456789";
43634 if(this.allowDecimals){
43635 allowed += this.decimalSeparator;
43638 if(this.allowNegative){
43642 if(this.thousandsDelimiter) {
43646 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43648 var keyPress = function(e){
43650 var k = e.getKey();
43652 var c = e.getCharCode();
43655 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43656 allowed.indexOf(String.fromCharCode(c)) === -1
43662 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43666 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43671 this.inputEl().on("keypress", keyPress, this);
43675 onTriggerClick : function(e)
43682 this.loadNext = false;
43684 if(this.isExpanded()){
43689 this.hasFocus = true;
43691 if(this.triggerAction == 'all') {
43692 this.doQuery(this.allQuery, true);
43696 this.doQuery(this.getRawValue());
43699 getCurrency : function()
43701 var v = this.currencyEl().getValue();
43706 restrictHeight : function()
43708 this.list.alignTo(this.currencyEl(), this.listAlign);
43709 this.list.alignTo(this.currencyEl(), this.listAlign);
43712 onViewClick : function(view, doFocus, el, e)
43714 var index = this.view.getSelectedIndexes()[0];
43716 var r = this.store.getAt(index);
43719 this.onSelect(r, index);
43723 onSelect : function(record, index){
43725 if(this.fireEvent('beforeselect', this, record, index) !== false){
43727 this.setFromCurrencyData(index > -1 ? record.data : false);
43731 this.fireEvent('select', this, record, index);
43735 setFromCurrencyData : function(o)
43739 this.lastCurrency = o;
43741 if (this.currencyField) {
43742 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43744 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43747 this.lastSelectionText = currency;
43749 //setting default currency
43750 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43751 this.setCurrency(this.defaultCurrency);
43755 this.setCurrency(currency);
43758 setFromData : function(o)
43762 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43764 this.setFromCurrencyData(c);
43769 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43771 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43774 this.setValue(value);
43778 setCurrency : function(v)
43780 this.currencyValue = v;
43783 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43788 setValue : function(v)
43790 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43796 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43798 this.inputEl().dom.value = (v == '') ? '' :
43799 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43801 if(!this.allowZero && v === '0') {
43802 this.hiddenEl().dom.value = '';
43803 this.inputEl().dom.value = '';
43810 getRawValue : function()
43812 var v = this.inputEl().getValue();
43817 getValue : function()
43819 return this.fixPrecision(this.parseValue(this.getRawValue()));
43822 parseValue : function(value)
43824 if(this.thousandsDelimiter) {
43826 r = new RegExp(",", "g");
43827 value = value.replace(r, "");
43830 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43831 return isNaN(value) ? '' : value;
43835 fixPrecision : function(value)
43837 if(this.thousandsDelimiter) {
43839 r = new RegExp(",", "g");
43840 value = value.replace(r, "");
43843 var nan = isNaN(value);
43845 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43846 return nan ? '' : value;
43848 return parseFloat(value).toFixed(this.decimalPrecision);
43851 decimalPrecisionFcn : function(v)
43853 return Math.floor(v);
43856 validateValue : function(value)
43858 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43862 var num = this.parseValue(value);
43865 this.markInvalid(String.format(this.nanText, value));
43869 if(num < this.minValue){
43870 this.markInvalid(String.format(this.minText, this.minValue));
43874 if(num > this.maxValue){
43875 this.markInvalid(String.format(this.maxText, this.maxValue));
43882 validate : function()
43884 if(this.disabled || this.allowBlank){
43889 var currency = this.getCurrency();
43891 if(this.validateValue(this.getRawValue()) && currency.length){
43896 this.markInvalid();
43900 getName: function()
43905 beforeBlur : function()
43911 var v = this.parseValue(this.getRawValue());
43918 onBlur : function()
43922 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43923 //this.el.removeClass(this.focusClass);
43926 this.hasFocus = false;
43928 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43932 var v = this.getValue();
43934 if(String(v) !== String(this.startValue)){
43935 this.fireEvent('change', this, v, this.startValue);
43938 this.fireEvent("blur", this);
43941 inputEl : function()
43943 return this.el.select('.roo-money-amount-input', true).first();
43946 currencyEl : function()
43948 return this.el.select('.roo-money-currency-input', true).first();
43951 hiddenEl : function()
43953 return this.el.select('input.hidden-number-input',true).first();
43957 * @class Roo.bootstrap.BezierSignature
43958 * @extends Roo.bootstrap.Component
43959 * Bootstrap BezierSignature class
43960 * This script refer to:
43961 * Title: Signature Pad
43963 * Availability: https://github.com/szimek/signature_pad
43966 * Create a new BezierSignature
43967 * @param {Object} config The config object
43970 Roo.bootstrap.BezierSignature = function(config){
43971 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43977 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43984 mouse_btn_down: true,
43987 * @cfg {int} canvas height
43989 canvas_height: '200px',
43992 * @cfg {float|function} Radius of a single dot.
43997 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44002 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44007 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44012 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44017 * @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.
44019 bg_color: 'rgba(0, 0, 0, 0)',
44022 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44024 dot_color: 'black',
44027 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44029 velocity_filter_weight: 0.7,
44032 * @cfg {function} Callback when stroke begin.
44037 * @cfg {function} Callback when stroke end.
44041 getAutoCreate : function()
44043 var cls = 'roo-signature column';
44046 cls += ' ' + this.cls;
44056 for(var i = 0; i < col_sizes.length; i++) {
44057 if(this[col_sizes[i]]) {
44058 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44068 cls: 'roo-signature-body',
44072 cls: 'roo-signature-body-canvas',
44073 height: this.canvas_height,
44074 width: this.canvas_width
44081 style: 'display: none'
44089 initEvents: function()
44091 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44093 var canvas = this.canvasEl();
44095 // mouse && touch event swapping...
44096 canvas.dom.style.touchAction = 'none';
44097 canvas.dom.style.msTouchAction = 'none';
44099 this.mouse_btn_down = false;
44100 canvas.on('mousedown', this._handleMouseDown, this);
44101 canvas.on('mousemove', this._handleMouseMove, this);
44102 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44104 if (window.PointerEvent) {
44105 canvas.on('pointerdown', this._handleMouseDown, this);
44106 canvas.on('pointermove', this._handleMouseMove, this);
44107 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44110 if ('ontouchstart' in window) {
44111 canvas.on('touchstart', this._handleTouchStart, this);
44112 canvas.on('touchmove', this._handleTouchMove, this);
44113 canvas.on('touchend', this._handleTouchEnd, this);
44116 Roo.EventManager.onWindowResize(this.resize, this, true);
44118 // file input event
44119 this.fileEl().on('change', this.uploadImage, this);
44126 resize: function(){
44128 var canvas = this.canvasEl().dom;
44129 var ctx = this.canvasElCtx();
44130 var img_data = false;
44132 if(canvas.width > 0) {
44133 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44135 // setting canvas width will clean img data
44138 var style = window.getComputedStyle ?
44139 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44141 var padding_left = parseInt(style.paddingLeft) || 0;
44142 var padding_right = parseInt(style.paddingRight) || 0;
44144 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44147 ctx.putImageData(img_data, 0, 0);
44151 _handleMouseDown: function(e)
44153 if (e.browserEvent.which === 1) {
44154 this.mouse_btn_down = true;
44155 this.strokeBegin(e);
44159 _handleMouseMove: function (e)
44161 if (this.mouse_btn_down) {
44162 this.strokeMoveUpdate(e);
44166 _handleMouseUp: function (e)
44168 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44169 this.mouse_btn_down = false;
44174 _handleTouchStart: function (e) {
44176 e.preventDefault();
44177 if (e.browserEvent.targetTouches.length === 1) {
44178 // var touch = e.browserEvent.changedTouches[0];
44179 // this.strokeBegin(touch);
44181 this.strokeBegin(e); // assume e catching the correct xy...
44185 _handleTouchMove: function (e) {
44186 e.preventDefault();
44187 // var touch = event.targetTouches[0];
44188 // _this._strokeMoveUpdate(touch);
44189 this.strokeMoveUpdate(e);
44192 _handleTouchEnd: function (e) {
44193 var wasCanvasTouched = e.target === this.canvasEl().dom;
44194 if (wasCanvasTouched) {
44195 e.preventDefault();
44196 // var touch = event.changedTouches[0];
44197 // _this._strokeEnd(touch);
44202 reset: function () {
44203 this._lastPoints = [];
44204 this._lastVelocity = 0;
44205 this._lastWidth = (this.min_width + this.max_width) / 2;
44206 this.canvasElCtx().fillStyle = this.dot_color;
44209 strokeMoveUpdate: function(e)
44211 this.strokeUpdate(e);
44213 if (this.throttle) {
44214 this.throttleStroke(this.strokeUpdate, this.throttle);
44217 this.strokeUpdate(e);
44221 strokeBegin: function(e)
44223 var newPointGroup = {
44224 color: this.dot_color,
44228 if (typeof this.onBegin === 'function') {
44232 this.curve_data.push(newPointGroup);
44234 this.strokeUpdate(e);
44237 strokeUpdate: function(e)
44239 var rect = this.canvasEl().dom.getBoundingClientRect();
44240 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44241 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44242 var lastPoints = lastPointGroup.points;
44243 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44244 var isLastPointTooClose = lastPoint
44245 ? point.distanceTo(lastPoint) <= this.min_distance
44247 var color = lastPointGroup.color;
44248 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44249 var curve = this.addPoint(point);
44251 this.drawDot({color: color, point: point});
44254 this.drawCurve({color: color, curve: curve});
44264 strokeEnd: function(e)
44266 this.strokeUpdate(e);
44267 if (typeof this.onEnd === 'function') {
44272 addPoint: function (point) {
44273 var _lastPoints = this._lastPoints;
44274 _lastPoints.push(point);
44275 if (_lastPoints.length > 2) {
44276 if (_lastPoints.length === 3) {
44277 _lastPoints.unshift(_lastPoints[0]);
44279 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44280 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44281 _lastPoints.shift();
44287 calculateCurveWidths: function (startPoint, endPoint) {
44288 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44289 (1 - this.velocity_filter_weight) * this._lastVelocity;
44291 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44294 start: this._lastWidth
44297 this._lastVelocity = velocity;
44298 this._lastWidth = newWidth;
44302 drawDot: function (_a) {
44303 var color = _a.color, point = _a.point;
44304 var ctx = this.canvasElCtx();
44305 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44307 this.drawCurveSegment(point.x, point.y, width);
44309 ctx.fillStyle = color;
44313 drawCurve: function (_a) {
44314 var color = _a.color, curve = _a.curve;
44315 var ctx = this.canvasElCtx();
44316 var widthDelta = curve.endWidth - curve.startWidth;
44317 var drawSteps = Math.floor(curve.length()) * 2;
44319 ctx.fillStyle = color;
44320 for (var i = 0; i < drawSteps; i += 1) {
44321 var t = i / drawSteps;
44327 var x = uuu * curve.startPoint.x;
44328 x += 3 * uu * t * curve.control1.x;
44329 x += 3 * u * tt * curve.control2.x;
44330 x += ttt * curve.endPoint.x;
44331 var y = uuu * curve.startPoint.y;
44332 y += 3 * uu * t * curve.control1.y;
44333 y += 3 * u * tt * curve.control2.y;
44334 y += ttt * curve.endPoint.y;
44335 var width = curve.startWidth + ttt * widthDelta;
44336 this.drawCurveSegment(x, y, width);
44342 drawCurveSegment: function (x, y, width) {
44343 var ctx = this.canvasElCtx();
44345 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44346 this.is_empty = false;
44351 var ctx = this.canvasElCtx();
44352 var canvas = this.canvasEl().dom;
44353 ctx.fillStyle = this.bg_color;
44354 ctx.clearRect(0, 0, canvas.width, canvas.height);
44355 ctx.fillRect(0, 0, canvas.width, canvas.height);
44356 this.curve_data = [];
44358 this.is_empty = true;
44363 return this.el.select('input',true).first();
44366 canvasEl: function()
44368 return this.el.select('canvas',true).first();
44371 canvasElCtx: function()
44373 return this.el.select('canvas',true).first().dom.getContext('2d');
44376 getImage: function(type)
44378 if(this.is_empty) {
44383 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44386 drawFromImage: function(img_src)
44388 var img = new Image();
44390 img.onload = function(){
44391 this.canvasElCtx().drawImage(img, 0, 0);
44396 this.is_empty = false;
44399 selectImage: function()
44401 this.fileEl().dom.click();
44404 uploadImage: function(e)
44406 var reader = new FileReader();
44408 reader.onload = function(e){
44409 var img = new Image();
44410 img.onload = function(){
44412 this.canvasElCtx().drawImage(img, 0, 0);
44414 img.src = e.target.result;
44417 reader.readAsDataURL(e.target.files[0]);
44420 // Bezier Point Constructor
44421 Point: (function () {
44422 function Point(x, y, time) {
44425 this.time = time || Date.now();
44427 Point.prototype.distanceTo = function (start) {
44428 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44430 Point.prototype.equals = function (other) {
44431 return this.x === other.x && this.y === other.y && this.time === other.time;
44433 Point.prototype.velocityFrom = function (start) {
44434 return this.time !== start.time
44435 ? this.distanceTo(start) / (this.time - start.time)
44442 // Bezier Constructor
44443 Bezier: (function () {
44444 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44445 this.startPoint = startPoint;
44446 this.control2 = control2;
44447 this.control1 = control1;
44448 this.endPoint = endPoint;
44449 this.startWidth = startWidth;
44450 this.endWidth = endWidth;
44452 Bezier.fromPoints = function (points, widths, scope) {
44453 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44454 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44455 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44457 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44458 var dx1 = s1.x - s2.x;
44459 var dy1 = s1.y - s2.y;
44460 var dx2 = s2.x - s3.x;
44461 var dy2 = s2.y - s3.y;
44462 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44463 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44464 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44465 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44466 var dxm = m1.x - m2.x;
44467 var dym = m1.y - m2.y;
44468 var k = l2 / (l1 + l2);
44469 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44470 var tx = s2.x - cm.x;
44471 var ty = s2.y - cm.y;
44473 c1: new scope.Point(m1.x + tx, m1.y + ty),
44474 c2: new scope.Point(m2.x + tx, m2.y + ty)
44477 Bezier.prototype.length = function () {
44482 for (var i = 0; i <= steps; i += 1) {
44484 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44485 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44487 var xdiff = cx - px;
44488 var ydiff = cy - py;
44489 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44496 Bezier.prototype.point = function (t, start, c1, c2, end) {
44497 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44498 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44499 + (3.0 * c2 * (1.0 - t) * t * t)
44500 + (end * t * t * t);
44505 throttleStroke: function(fn, wait) {
44506 if (wait === void 0) { wait = 250; }
44508 var timeout = null;
44512 var later = function () {
44513 previous = Date.now();
44515 result = fn.apply(storedContext, storedArgs);
44517 storedContext = null;
44521 return function wrapper() {
44523 for (var _i = 0; _i < arguments.length; _i++) {
44524 args[_i] = arguments[_i];
44526 var now = Date.now();
44527 var remaining = wait - (now - previous);
44528 storedContext = this;
44530 if (remaining <= 0 || remaining > wait) {
44532 clearTimeout(timeout);
44536 result = fn.apply(storedContext, storedArgs);
44538 storedContext = null;
44542 else if (!timeout) {
44543 timeout = window.setTimeout(later, remaining);